nephila / python-taiga

:evergreen_tree: Python module for communicating with the Taiga API
https://python-taiga.readthedocs.io
MIT License
96 stars 41 forks source link

Feature interaction: project duplicate + swimlanes #169

Open psybers opened 10 months ago

psybers commented 10 months ago

Description

It appears there is a bit of a feature interaction between the ability to duplicate a project (#161) and the new swimlanes support (#162). The issue is that when duplicating a project, Taiga (apparently) returns a swimlanes parameter with no value. So Python sets the value to None and then attempts to call parse with that value. This results in the following crash:

Traceback (most recent call last):
  File "main.py", line 108, in <module>
    newProject = project.duplicate(name=newProjectName,
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/taiga/models/models.py", line 1769, in duplicate
    return self.parse(self.requester, response.json())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/taiga/models/base.py", line 199, in parse
    entry[key_to_parse] = cls_to_parse.parse(requester, entry[key_to_parse])
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/taiga/models/base.py", line 107, in parse
    for entry in entries:
TypeError: 'NoneType' object is not iterable

The problem is in base.py on line 107: https://github.com/nephila/python-taiga/blob/dce5ae0141b87a571389ae95e80d7bf46d48fc23/taiga/models/base.py#L107

Here, entries winds up being None. I believe this is really a shortcoming of the Taiga API response. It should either not send swimlanes in the response or it should send it with an empty list. At any rate, I would recommend placing an if entries: before this line to avoid problems such as this. In this case, it would simply wind up with an empty swimlanes attribute, which is correct.

Steps to reproduce

Call duplicate() on a project:

api = TaigaAPI()
api.auth(username=username, password=password)
project = api.projects.get_by_slug('nephila')
newProject = project.duplicate(name='test', description='This is a test')

Versions

python-taiga: 1.3.0

Expected behaviour

No crash. The project duplicates and returns a proper Project object.

Actual behaviour

Crashes. Though the project is duplicated on Taiga's side.

protoroto commented 10 months ago

@psybers Thanks for the detailed explaination: @PSzczepanski1996 already opened a PR fixing this, but I'd love to have a test covering this case. @yakky what do you think? We could mock a response after a duplicate() with swimlanes parameter empty (like we do in all other test cases, like https://github.com/nephila/python-taiga/blob/master/tests/test_epics.py#L16 here, maybe taking a real response - with fake data, of course) and assert that no exception is thrown (and the project is actually duplicated)

protoroto commented 10 months ago

@psybers Actually, I don't have a running Taiga + Python Taiga instance to see what kind of response a duplicate() will return: if you provide me one, I can add a test to @PSzczepanski1996 PR to cover this case.

PSzczepanski1996 commented 10 months ago

That's fine, I'm little busy with work at my workplace so if you can write test - just do that. Thanks!

psybers commented 9 months ago

@protoroto according to the API docs (https://docs.taiga.io/api.html#projects-duplicate) it just returns a project detail object:

https://docs.taiga.io/api.html#object-project-detail

protoroto commented 9 months ago

@psybers @PSzczepanski1996 Sorry for the delay, sadly I didn't had much time to work on this. I'll try next week to write a test and add it to @PSzczepanski1996 pr

kodyo commented 8 months ago

More observation: This issue doesn't appear to be limited to projects created from duplicate(). If a project has never had a swimlane created, the api.projects.get_by_slug() function crashes when it tries to enumerate swimlanes. The parse() function in base.py was failing because there are no swimlanes to enumerate. In my case, the Kanban module had not been enabled, so swimlane functionality wasn't even visible.

The get_by_slug() function succeeds after enabling the Kanban module in a project, manually creating a swimlane, then disabling the Kanban module.

LuminatiHD commented 4 months ago

hello, what is the status on this? I'm experiencing the same errors.

InsaneCake commented 4 months ago

Have issue with api.projects.get_by_slug(). Creating swimlane works as a workaround (thanks @kodyo), but permanent keeping single swimlane just for script leads to worse usability overall due to limited kanban space on web page.

For those who keep getting TypeError: 'NoneType' object is not iterable and don't need new duplication and swimlane functionality - rollback to 1.2.0 is also an option: pip install --force-reinstall -v "python-taiga==1.2.0"