Repository: nephila/python-taiga

Issue:
Feature interaction: project duplicate + swimlanes
## 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:

```python
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:

```python
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.

Hints from issue comments:
@protoroto: @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: @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.@memegauste: That's fine, I'm little busy with work at my workplace so if you can write test - just do that.
Thanks!@psybers: @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: @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: 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: hello, what is the status on this? I'm experiencing the same errors.@InsaneCake: 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"`

@protoroto: Hi everyone: sorry for the delayed answer, but I haven't had time for looking into this.
As I said before, I'd like to add a test for this, but I haven't had time.
I'll try to look into this next week, I should be able to at least try to test the `get_project_by_slug` with a project with no swimlanes and see what is the API response from Taiga, and build a test from there.@protoroto: @InsaneCake @LuminatiHD @kodyo @psybers @PSzczepanski1996 I've just released 1.3.1 version that should address this issue. Could you please check if everything is working as expected now?@psybers: I *think* this is fixed.  The reason I am not 100% sure is that my test code (the code above to reproduce) gives a HTTP 500 error on some of my projects. But it did work for *one* project.

So I think the bug, as reported, is probably fixed but there is some other issue going on with duplicate().  Or the Taiga servers are having an issue.@psybers: Again, there was no error for that one case... but it didn't duplicate the swimlanes (as I expected).@psybers: I think the lack of duplicated swimlanes is just a Taiga API thing.  So nothing to fix there.  But I still don't know why most of the projects I tried to duplicate now give a 500 error.  But again, it feels like that is more of an issue on their side and this bug (as reported) feels fixed to me.@protoroto: @psybers Thanks for the feedback: I'm going to leave this open for a few days and then I'm going to close it, if you have new info about this please report again and we'll try to look into it.@protoroto: I'm closing this, because it should be fixed with 1.3.1 release. If anyone experience the same problem, feel free to open a new issue referencing this one. 

Solution patch:
diff --git a/taiga/models/base.py b/taiga/models/base.py
index 7e08297..3ec60b1 100644
--- a/taiga/models/base.py
+++ b/taiga/models/base.py
@@ -104,15 +104,17 @@ class ListResource(Resource):
     def parse(cls, requester, entries):
         """Parse a JSON array into a list of model instances."""
         result_entries = SearchableList()
-        for entry in entries:
-            result_entries.append(cls.instance.parse(requester, entry))
+        if entries:
+            for entry in entries:
+                result_entries.append(cls.instance.parse(requester, entry))
         return result_entries
 
     def parse_list(self, entries):
         """Parse a JSON array into a list of model instances."""
         result_entries = SearchableList()
-        for entry in entries:
-            result_entries.append(self.instance.parse(self.requester, entry))
+        if entries:
+            for entry in entries:
+                result_entries.append(self.instance.parse(self.requester, entry))
         return result_entries
 
 


Test patch:
diff --git a/tests/resources/project_details_success_no_swimlanes.json b/tests/resources/project_details_success_no_swimlanes.json
new file mode 100644
index 0000000..e0591c1
--- /dev/null
+++ b/tests/resources/project_details_success_no_swimlanes.json
@@ -0,0 +1,823 @@
+{
+    "likes": 1,
+    "is_liked": true,
+    "is_watched": true,
+    "watchers": [
+        5,
+        4,
+        6,
+        7,
+        8,
+        9,
+        10,
+        11,
+        12,
+        13,
+        14
+    ],
+    "tags": [],
+    "anon_permissions": [],
+    "public_permissions": [],
+    "my_permissions": [
+        "add_wiki_page",
+        "add_member",
+        "view_project",
+        "admin_project_values",
+        "view_wiki_pages",
+        "view_wiki_links",
+        "admin_roles",
+        "add_task",
+        "remove_member",
+        "delete_task",
+        "add_comments_to_issue",
+        "add_milestone",
+        "view_tasks",
+        "add_us",
+        "delete_us",
+        "modify_task",
+        "add_comments_to_task",
+        "add_us_to_project",
+        "view_us",
+        "add_issue",
+        "modify_us",
+        "view_issues",
+        "modify_project",
+        "view_milestones",
+        "request_membership",
+        "modify_issue",
+        "modify_wiki_link",
+        "delete_project",
+        "delete_milestone",
+        "delete_wiki_page",
+        "delete_wiki_link",
+        "add_comments_to_us",
+        "modify_milestone",
+        "delete_issue",
+        "add_wiki_link",
+        "modify_wiki_page"
+    ],
+    "i_am_owner": true,
+    "tags_colors": {
+        "voluptatem": "#00d60c",
+        "vero": "#74e191",
+        "nam": "#ce4004",
+        "velit": "#790ea4",
+        "libero": "#5b20bf",
+        "exercitationem": "#ac7c74",
+        "odio": "#ad7fa8",
+        "sit": "#abdcde",
+        "tempore": "#ae2670",
+        "quo": "#857670",
+        "commodi": "#edd400",
+        "necessitatibus": "#84e3b6",
+        "ipsa": "#ffa8ed",
+        "illo": "#3531fd",
+        "dolorum": "#db7ec2",
+        "natus": "#e610c1",
+        "eaque": "#3e7c66",
+        "omnis": "#fc9548",
+        "laboriosam": "#b2966d",
+        "harum": "#b42d3c",
+        "inventore": "#2fbc07",
+        "animi": "#d93411",
+        "excepturi": "#5c3c96",
+        "at": "#27e90d",
+        "voluptatum": "#02d22f",
+        "saepe": "#b87b67",
+        "provident": "#7fdcf2",
+        "repudiandae": "#3a2b71",
+        "vitae": "#d9fe5e",
+        "et": "#729fcf",
+        "fuga": "#e86797",
+        "voluptates": "#2e3436",
+        "quaerat": "#0b4425",
+        "consectetur": "#97176f",
+        "obcaecati": "#9ccd46",
+        "eos": "#5c3566",
+        "non": "#37031f",
+        "quis": "#223610",
+        "reprehenderit": "#6c82c6",
+        "recusandae": "#4e9a06",
+        "doloribus": "#fb1b00",
+        "esse": "#d77661",
+        "quod": "#0e5b24",
+        "iusto": "#3a10e8",
+        "illum": "#898c66",
+        "ullam": "#98ad13",
+        "eligendi": "#5d8273",
+        "aspernatur": "#82854c",
+        "aut": "#fcaf3e",
+        "dolorem": "#604860",
+        "odit": "#e2b537",
+        "magnam": "#d1fac1",
+        "iste": "#491b3a",
+        "quisquam": "#ebca0b",
+        "fugit": "#9345df",
+        "nostrum": "#f57900",
+        "voluptatibus": "#681ad4",
+        "molestiae": "#1415dc",
+        "expedita": "#c4a000",
+        "reiciendis": "#560ff6",
+        "itaque": "#090d7d",
+        "consequatur": "#fce94f",
+        "deserunt": "#e7b695",
+        "mollitia": "#002e7f",
+        "impedit": "#cde1f0",
+        "possimus": "#a40000",
+        "repellendus": "#13f068",
+        "quae": "#d91a8b",
+        "labore": "#6fdf52",
+        "nobis": "#91c2a9",
+        "iure": "#019320",
+        "amet": "#cc0000",
+        "incidunt": "#3099ec",
+        "ducimus": "#ea6bb9",
+        "aperiam": "#a2b100",
+        "modi": "#494e30",
+        "rem": "#688119",
+        "praesentium": "#0cd131",
+        "quam": "#0149d1",
+        "est": "#665de1",
+        "aliquid": "#f01df5",
+        "sint": "#3b2404",
+        "in": "#af10ef",
+        "ea": "#2c80b2",
+        "fugiat": "#1c563a",
+        "neque": "#150607",
+        "temporibus": "#a2c51a",
+        "sed": "#d3d7cf",
+        "nihil": "#98a352",
+        "voluptate": "#b0eff0",
+        "molestias": "#92db0b",
+        "dignissimos": "#79b3c9",
+        "quasi": "#73d216",
+        "a": "#86f7e4",
+        "asperiores": "#a69134",
+        "totam": "#560a5d",
+        "placeat": "#75507b",
+        "veniam": "#4661cf",
+        "id": "#87ea5d",
+        "minus": "#59b653",
+        "earum": "#24bec9",
+        "blanditiis": "#65026b",
+        "assumenda": "#52b91a",
+        "optio": "#7617d3",
+        "facilis": "#0f6b6b",
+        "porro": "#05175b",
+        "maxime": "#1acc29",
+        "nisi": "#ef7fdc",
+        "ut": "#e74669",
+        "ipsam": "#fa74af",
+        "rerum": "#b1c629",
+        "adipisci": "#257dec",
+        "dolore": "#61b076",
+        "soluta": "#1398ab",
+        "facere": "#113f4a",
+        "quidem": "#3465a4",
+        "aliquam": "#631249",
+        "corrupti": "#432493",
+        "dicta": "#939b44",
+        "accusantium": "#b36f86",
+        "nemo": "#e81498",
+        "debitis": "#9631e4",
+        "similique": "#204a87",
+        "maiores": "#cbb2b3",
+        "deleniti": "#6188db",
+        "ex": "#e06613",
+        "voluptas": "#729359",
+        "perferendis": "#888a85",
+        "doloremque": "#61405d",
+        "ratione": "#570ce3",
+        "repellat": "#807389",
+        "cupiditate": "#144bba",
+        "ab": "#da2361",
+        "veritatis": "#768459",
+        "laborum": "#67eac4",
+        "magni": "#429e6f",
+        "officia": "#c4f027",
+        "cum": "#ab14d9",
+        "sequi": "#9f6274",
+        "architecto": "#9d1e93",
+        "error": "#11f957",
+        "tenetur": "#351c86",
+        "atque": "#713547",
+        "nulla": "#894727",
+        "qui": "#61f611",
+        "perspiciatis": "#afb825",
+        "nesciunt": "#4c8404",
+        "beatae": "#b844bd",
+        "dolores": "#7fea8e",
+        "ipsum": "#ce5c00",
+        "unde": "#8ae234",
+        "cumque": "#ad75ec",
+        "tempora": "#b55d30",
+        "officiis": "#964862",
+        "autem": "#5e8c91",
+        "distinctio": "#1f8960",
+        "consequuntur": "#ce24ec",
+        "enim": "#ef2929"
+    },
+    "total_closed_milestones": 0,
+    "notify_level": 1,
+    "us_statuses": [
+        {
+            "id": 1,
+            "name": "New",
+            "slug": "new",
+            "order": 1,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#999999",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "Ready",
+            "slug": "ready",
+            "order": 2,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#ff8a84",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "In progress",
+            "slug": "in-progress",
+            "order": 3,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#ff9900",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 4,
+            "name": "Ready for test",
+            "slug": "ready-for-test",
+            "order": 4,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#fcc000",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 5,
+            "name": "Done",
+            "slug": "done",
+            "order": 5,
+            "is_closed": true,
+            "is_archived": false,
+            "color": "#669900",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 6,
+            "name": "Archived",
+            "slug": "archived",
+            "order": 6,
+            "is_closed": true,
+            "is_archived": true,
+            "color": "#5c3566",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 241,
+            "name": "lol",
+            "slug": "lol",
+            "order": 7,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#999999",
+            "wip_limit": null,
+            "project": 1
+        },
+        {
+            "id": 242,
+            "name": "asd",
+            "slug": "asd",
+            "order": 8,
+            "is_closed": false,
+            "is_archived": false,
+            "color": "#999999",
+            "wip_limit": null,
+            "project": 1
+        }
+    ],
+    "points": [
+        {
+            "id": 1,
+            "name": "?",
+            "order": 1,
+            "value": null,
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "0",
+            "order": 2,
+            "value": 0.0,
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "1/2",
+            "order": 3,
+            "value": 0.5,
+            "project": 1
+        },
+        {
+            "id": 4,
+            "name": "1",
+            "order": 4,
+            "value": 1.0,
+            "project": 1
+        },
+        {
+            "id": 5,
+            "name": "2",
+            "order": 5,
+            "value": 2.0,
+            "project": 1
+        },
+        {
+            "id": 6,
+            "name": "3",
+            "order": 6,
+            "value": 3.0,
+            "project": 1
+        },
+        {
+            "id": 7,
+            "name": "5",
+            "order": 7,
+            "value": 5.0,
+            "project": 1
+        },
+        {
+            "id": 8,
+            "name": "8",
+            "order": 8,
+            "value": 8.0,
+            "project": 1
+        },
+        {
+            "id": 9,
+            "name": "10",
+            "order": 9,
+            "value": 10.0,
+            "project": 1
+        },
+        {
+            "id": 10,
+            "name": "15",
+            "order": 10,
+            "value": 15.0,
+            "project": 1
+        },
+        {
+            "id": 11,
+            "name": "20",
+            "order": 11,
+            "value": 20.0,
+            "project": 1
+        },
+        {
+            "id": 12,
+            "name": "40",
+            "order": 12,
+            "value": 40.0,
+            "project": 1
+        }
+    ],
+    "task_statuses": [
+        {
+            "id": 1,
+            "name": "New",
+            "slug": "new",
+            "order": 1,
+            "is_closed": false,
+            "color": "#999999",
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "In progress",
+            "slug": "in-progress",
+            "order": 2,
+            "is_closed": false,
+            "color": "#ff9900",
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "Ready for test",
+            "slug": "ready-for-test",
+            "order": 3,
+            "is_closed": true,
+            "color": "#ffcc00",
+            "project": 1
+        },
+        {
+            "id": 4,
+            "name": "Closed",
+            "slug": "closed",
+            "order": 4,
+            "is_closed": true,
+            "color": "#669900",
+            "project": 1
+        },
+        {
+            "id": 5,
+            "name": "Needs Info",
+            "slug": "needs-info",
+            "order": 5,
+            "is_closed": false,
+            "color": "#999999",
+            "project": 1
+        }
+    ],
+    "issue_statuses": [
+        {
+            "id": 2,
+            "name": "In progress",
+            "slug": "in-progress",
+            "order": 1,
+            "is_closed": false,
+            "color": "#5E8C6A",
+            "project": 1
+        },
+        {
+            "id": 4,
+            "name": "Closed",
+            "slug": "closed",
+            "order": 3,
+            "is_closed": true,
+            "color": "#BFB35A",
+            "project": 1
+        },
+        {
+            "id": 5,
+            "name": "Needs Info",
+            "slug": "needs-info",
+            "order": 4,
+            "is_closed": false,
+            "color": "#89BAB4",
+            "project": 1
+        },
+        {
+            "id": 6,
+            "name": "Rejected",
+            "slug": "rejected",
+            "order": 5,
+            "is_closed": true,
+            "color": "#CC0000",
+            "project": 1
+        },
+        {
+            "id": 7,
+            "name": "Postponed",
+            "slug": "postponed",
+            "order": 6,
+            "is_closed": false,
+            "color": "#666666",
+            "project": 1
+        },
+        {
+            "id": 428,
+            "name": "Workinon",
+            "slug": "workinon",
+            "order": 7,
+            "is_closed": false,
+            "color": "#999999",
+            "project": 1
+        },
+        {
+            "id": 429,
+            "name": "Rambleon",
+            "slug": "rambleon",
+            "order": 8,
+            "is_closed": false,
+            "color": "#999999",
+            "project": 1
+        },
+        {
+            "id": 430,
+            "name": "ClosedUp",
+            "slug": "closedup",
+            "order": 10,
+            "is_closed": true,
+            "color": "#999999",
+            "project": 1
+        }
+    ],
+    "issue_types": [
+        {
+            "id": 1,
+            "name": "Bug",
+            "order": 1,
+            "color": "#89BAB4",
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "Question",
+            "order": 2,
+            "color": "#ba89a8",
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "Enhancement",
+            "order": 3,
+            "color": "#89a8ba",
+            "project": 1
+        }
+    ],
+    "priorities": [
+        {
+            "id": 1,
+            "name": "Low",
+            "order": 1,
+            "color": "#666666",
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "Normal",
+            "order": 3,
+            "color": "#669933",
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "High",
+            "order": 5,
+            "color": "#CC0000",
+            "project": 1
+        }
+    ],
+    "severities": [
+        {
+            "id": 1,
+            "name": "Wishlist",
+            "order": 1,
+            "color": "#666666",
+            "project": 1
+        },
+        {
+            "id": 2,
+            "name": "Minor",
+            "order": 2,
+            "color": "#669933",
+            "project": 1
+        },
+        {
+            "id": 3,
+            "name": "Normal",
+            "order": 3,
+            "color": "#0000FF",
+            "project": 1
+        },
+        {
+            "id": 4,
+            "name": "Important",
+            "order": 4,
+            "color": "#FFA500",
+            "project": 1
+        },
+        {
+            "id": 5,
+            "name": "Critical",
+            "order": 5,
+            "color": "#CC0000",
+            "project": 1
+        }
+    ],
+    "userstory_custom_attributes": [],
+    "task_custom_attributes": [],
+    "issue_custom_attributes": [],
+    "roles": [
+        {
+            "id": 1,
+            "name": "UX",
+            "slug": "ux",
+            "order": 10,
+            "computable": true
+        },
+        {
+            "id": 2,
+            "name": "Design",
+            "slug": "design",
+            "order": 20,
+            "computable": true
+        },
+        {
+            "id": 3,
+            "name": "Front",
+            "slug": "front",
+            "order": 30,
+            "computable": true
+        },
+        {
+            "id": 4,
+            "name": "Back",
+            "slug": "back",
+            "order": 40,
+            "computable": true
+        },
+        {
+            "id": 5,
+            "name": "Product Owner",
+            "slug": "product-owner",
+            "order": 50,
+            "computable": true
+        },
+        {
+            "id": 6,
+            "name": "Stakeholder",
+            "slug": "stakeholder",
+            "order": 60,
+            "computable": false
+        }
+    ],
+    "swimlanes": null,
+    "members": [
+        {
+            "id": 10,
+            "username": "user5",
+            "full_name": "Alicia Diaz",
+            "full_name_display": "Alicia Diaz",
+            "color": "#C0FF33",
+            "photo": "//www.gravatar.com/avatar/e2cc11c9138524224f87aa72c1a343db?size=80",
+            "is_active": true,
+            "role_name": "Front",
+            "user": 10,
+            "role": 3,
+            "is_owner": true
+        },
+        {
+            "id": 9,
+            "username": "user4",
+            "full_name": "Alicia Flores",
+            "full_name_display": "Alicia Flores",
+            "color": "#FFF8E7",
+            "photo": "//www.gravatar.com/avatar/555b9a0deff8123ed6e7672f7dbaed75?size=80",
+            "is_active": true,
+            "role_name": "Back",
+            "user": 9,
+            "role": 4,
+            "is_owner": true
+        },
+        {
+            "id": 4,
+            "username": "admin",
+            "full_name": "Andre Stagi",
+            "full_name_display": "Andre Stagi",
+            "color": "",
+            "photo": "//www.gravatar.com/avatar/d44459efd0fcf29bab2e6b7c1e98c8a0?size=80",
+            "is_active": true,
+            "role_name": "Design",
+            "user": 4,
+            "role": 2,
+            "is_owner": true
+        },
+        {
+            "id": 6,
+            "username": "user1",
+            "full_name": "Josefa Serrano",
+            "full_name_display": "Josefa Serrano",
+            "color": "#FFCC00",
+            "photo": "//www.gravatar.com/avatar/0163263aeacdbb0a723152ee0d7225b2?size=80",
+            "is_active": true,
+            "role_name": "Product Owner",
+            "user": 6,
+            "role": 5,
+            "is_owner": true
+        },
+        {
+            "id": 12,
+            "username": "user7",
+            "full_name": "Josefina Reyes",
+            "full_name_display": "Josefina Reyes",
+            "color": "#FFFF00",
+            "photo": "//www.gravatar.com/avatar/66ceeab4ce04e47a95f64cb2be785376?size=80",
+            "is_active": true,
+            "role_name": "UX",
+            "user": 12,
+            "role": 1,
+            "is_owner": true
+        },
+        {
+            "id": 11,
+            "username": "user6",
+            "full_name": "Mar Serrano",
+            "full_name_display": "Mar Serrano",
+            "color": "#FC8EAC",
+            "photo": "//www.gravatar.com/avatar/ba0e3558f431a5372cf5e225eec0992f?size=80",
+            "is_active": true,
+            "role_name": "Stakeholder",
+            "user": 11,
+            "role": 6,
+            "is_owner": true
+        },
+        {
+            "id": 8,
+            "username": "user3",
+            "full_name": "Margarita Rubio",
+            "full_name_display": "Margarita Rubio",
+            "color": "#40826D",
+            "photo": "//www.gravatar.com/avatar/fba51849dd340c3bc8571c3667b25861?size=80",
+            "is_active": true,
+            "role_name": "Back",
+            "user": 8,
+            "role": 4,
+            "is_owner": false
+        },
+        {
+            "id": 7,
+            "username": "user2",
+            "full_name": "Rafael Herrero",
+            "full_name_display": "Rafael Herrero",
+            "color": "#0F0F0F",
+            "photo": "//www.gravatar.com/avatar/e3118e4dc62fd3a8821725a73a94ced1?size=80",
+            "is_active": true,
+            "role_name": "Design",
+            "user": 7,
+            "role": 2,
+            "is_owner": true
+        },
+        {
+            "id": 5,
+            "username": "user6532909695705815086",
+            "full_name": "Silvia Soto",
+            "full_name_display": "Silvia Soto",
+            "color": "#2099DB",
+            "photo": "//www.gravatar.com/avatar/f64db24896b923753d440e23faa856aa?size=80",
+            "is_active": true,
+            "role_name": "Product Owner",
+            "user": 5,
+            "role": 5,
+            "is_owner": true
+        },
+        {
+            "id": 14,
+            "username": "user9",
+            "full_name": "Teresa Gallardo",
+            "full_name_display": "Teresa Gallardo",
+            "color": "#FFCC00",
+            "photo": "//www.gravatar.com/avatar/7c5a4c10628c2a203b24e2d10e3e28e1?size=80",
+            "is_active": true,
+            "role_name": "Design",
+            "user": 14,
+            "role": 2,
+            "is_owner": true
+        },
+        {
+            "id": 13,
+            "username": "user8",
+            "full_name": "Victoria Nu\u00f1ez",
+            "full_name_display": "Victoria Nu\u00f1ez",
+            "color": "#FFF8E7",
+            "photo": "//www.gravatar.com/avatar/8c83c46614d8d46c0cb3096c9deb6e1f?size=80",
+            "is_active": true,
+            "role_name": "Design",
+            "user": 13,
+            "role": 2,
+            "is_owner": true
+        }
+    ],
+    "id": 1,
+    "default_points": 1,
+    "default_us_status": 1,
+    "default_task_status": 1,
+    "default_priority": 2,
+    "default_severity": 3,
+    "default_swimlane": null,
+    "default_issue_status": 429,
+    "default_issue_type": 1,
+    "name": "Project Example 0",
+    "slug": "user6532909695705815086-project-example-0",
+    "description": "Project example 0 description",
+    "created_date": "2015-02-17T18:14:14+0000",
+    "modified_date": "2015-09-10T10:56:22+0000",
+    "owner": 5,
+    "total_milestones": 5,
+    "total_story_points": 1572.0,
+    "is_backlog_activated": true,
+    "is_kanban_activated": true,
+    "is_wiki_activated": true,
+    "is_issues_activated": true,
+    "videoconferences": null,
+    "videoconferences_extra_data": null,
+    "creation_template": 1,
+    "is_private": true,
+    "userstories_csv_uuid": null,
+    "tasks_csv_uuid": null,
+    "issues_csv_uuid": null
+}
diff --git a/tests/test_projects.py b/tests/test_projects.py
index 2777ec1..edd3342 100644
--- a/tests/test_projects.py
+++ b/tests/test_projects.py
@@ -48,6 +48,19 @@ class TestProjects(unittest.TestCase):
             "/{endpoint}/by_slug?slug={slug}", endpoint="projects", slug="my_slug"
         )
 
+    @patch("taiga.requestmaker.RequestMaker.get")
+    def test_get_project_by_slug_no_swimlanes(self, mock_requestmaker_get):
+        mock_requestmaker_get.return_value = MockResponse(
+            200, create_mock_json("tests/resources/project_details_success_no_swimlanes.json")
+        )
+        api = TaigaAPI(token="f4k3")
+        project = api.projects.get_by_slug("my_slug")
+        self.assertTrue(isinstance(project, Project))
+        self.assertEqual(project.name, "Project Example 0")
+        mock_requestmaker_get.assert_called_with(
+            "/{endpoint}/by_slug?slug={slug}", endpoint="projects", slug="my_slug"
+        )
+
     @patch("taiga.requestmaker.RequestMaker.get")
     def test_get_item_by_ref(self, mock_requestmaker_get):
         mock_requestmaker_get.return_value = MockResponse(