Repository: python-gitlab/python-gitlab

Issue:
Cannot list package files
## Description of the problem, including code/CLI snippet

[Listing package files](https://docs.gitlab.com/ee/api/packages.html#list-package-files) appears to be unsupported. The API endpoint was introduced in GitLab 11.8.

## Expected Behavior

Listing package files should be possible.

## Actual Behavior

Listing package files is not possible.

## Specifications

  - python-gitlab version: 2.6.0
  - API version you are using (v3/v4): v4
  - Gitlab server version (or gitlab.com): gitlab.com


PR incoming.


Discussion:

Resolving patch:
diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py
--- a/gitlab/v4/objects/packages.py
+++ b/gitlab/v4/objects/packages.py
@@ -1,12 +1,13 @@
 from gitlab.base import RESTManager, RESTObject
 from gitlab.mixins import DeleteMixin, GetMixin, ListMixin, ObjectDeleteMixin
 
-
 __all__ = [
     "GroupPackage",
     "GroupPackageManager",
     "ProjectPackage",
     "ProjectPackageManager",
+    "ProjectPackageFile",
+    "ProjectPackageFileManager",
 ]
 
 
@@ -28,7 +29,7 @@ class GroupPackageManager(ListMixin, RESTManager):
 
 
 class ProjectPackage(ObjectDeleteMixin, RESTObject):
-    pass
+    _managers = (("package_files", "ProjectPackageFileManager"),)
 
 
 class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
@@ -41,3 +42,13 @@ class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
         "package_type",
         "package_name",
     )
+
+
+class ProjectPackageFile(RESTObject):
+    pass
+
+
+class ProjectPackageFileManager(ListMixin, RESTManager):
+    _path = "/projects/%(project_id)s/packages/%(package_id)s/package_files"
+    _obj_cls = ProjectPackageFile
+    _from_parent_attrs = {"project_id": "project_id", "package_id": "id"}


Test patch:
diff --git a/gitlab/tests/objects/test_packages.py b/gitlab/tests/objects/test_packages.py
--- a/gitlab/tests/objects/test_packages.py
+++ b/gitlab/tests/objects/test_packages.py
@@ -6,7 +6,7 @@
 import pytest
 import responses
 
-from gitlab.v4.objects import GroupPackage, ProjectPackage
+from gitlab.v4.objects import GroupPackage, ProjectPackage, ProjectPackageFile
 
 
 package_content = {
@@ -54,6 +54,51 @@
     ],
 }
 
+package_file_content = [
+    {
+        "id": 25,
+        "package_id": 1,
+        "created_at": "2018-11-07T15:25:52.199Z",
+        "file_name": "my-app-1.5-20181107.152550-1.jar",
+        "size": 2421,
+        "file_md5": "58e6a45a629910c6ff99145a688971ac",
+        "file_sha1": "ebd193463d3915d7e22219f52740056dfd26cbfe",
+        "pipelines": [
+            {
+                "id": 123,
+                "status": "pending",
+                "ref": "new-pipeline",
+                "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
+                "web_url": "https://example.com/foo/bar/pipelines/47",
+                "created_at": "2016-08-11T11:28:34.085Z",
+                "updated_at": "2016-08-11T11:32:35.169Z",
+                "user": {
+                    "name": "Administrator",
+                    "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+                },
+            }
+        ],
+    },
+    {
+        "id": 26,
+        "package_id": 1,
+        "created_at": "2018-11-07T15:25:56.776Z",
+        "file_name": "my-app-1.5-20181107.152550-1.pom",
+        "size": 1122,
+        "file_md5": "d90f11d851e17c5513586b4a7e98f1b2",
+        "file_sha1": "9608d068fe88aff85781811a42f32d97feb440b5",
+    },
+    {
+        "id": 27,
+        "package_id": 1,
+        "created_at": "2018-11-07T15:26:00.556Z",
+        "file_name": "maven-metadata.xml",
+        "size": 767,
+        "file_md5": "6dfd0cce1203145a927fef5e3a1c650c",
+        "file_sha1": "d25932de56052d320a8ac156f745ece73f6a8cd2",
+    },
+]
+
 
 @pytest.fixture
 def resp_list_packages():
@@ -94,6 +139,21 @@ def resp_delete_package(no_content):
         yield rsps
 
 
+@pytest.fixture
+def resp_list_package_files():
+    with responses.RequestsMock() as rsps:
+        rsps.add(
+            method=responses.GET,
+            url=re.compile(
+                r"http://localhost/api/v4/projects/1/packages/1/package_files"
+            ),
+            json=package_file_content,
+            content_type="application/json",
+            status=200,
+        )
+        yield rsps
+
+
 def test_list_project_packages(project, resp_list_packages):
     packages = project.packages.list()
     assert isinstance(packages, list)
@@ -117,3 +177,11 @@ def test_get_project_package(project, resp_get_package):
 def test_delete_project_package(project, resp_delete_package):
     package = project.packages.get(1, lazy=True)
     package.delete()
+
+
+def test_list_project_package_files(project, resp_list_package_files):
+    package = project.packages.get(1, lazy=True)
+    package_files = package.package_files.list()
+    assert isinstance(package_files, list)
+    assert isinstance(package_files[0], ProjectPackageFile)
+    assert package_files[0].id == 25