Skip to content

Add option to view versionless packages in API

Steve Abrams requested to merge versionless-package-api-option into master

🔎 What does this MR do?

Currently, the Package API allows users to fetch lists of their packages. However, the list of packages returned excludes any packages that do not have a version. The same is true for when users view their packages through the UI.

There are some known bugs (for example #11424 (closed)) that will cause versionless packages to be created. Once created, users have no way to view them, which means they cannot easily access or delete them.

This MR adds an optional param to the API to allow users to view their packages and include any versionless packages in the results.

🐘 Database

This MR does not introduce any new queries. It makes the .has_version (WHERE version IS NOT NULL) scope optional.

It does change the order of two scopes in the GroupPackagesFinder, however this does not have any effect on the generated query or plan:

[2] pry(main)> Packages::Package.has_version.sort_by_attribute('name_asc').explain
  Packages::Package Load (0.6ms)  SELECT "packages_packages".* FROM "packages_packages" WHERE "packages_packages"."version" IS NOT NULL ORDER BY name ASC
=> EXPLAIN for: SELECT "packages_packages".* FROM "packages_packages" WHERE "packages_packages"."version" IS NOT NULL ORDER BY name ASC
                                          QUERY PLAN
-----------------------------------------------------------------------------------------------
 Index Scan using package_name_index on packages_packages  (cost=0.14..5.64 rows=100 width=71)
   Filter: (version IS NOT NULL)
(2 rows)

[3] pry(main)> Packages::Package.sort_by_attribute('name_asc').has_version.explain
  Packages::Package Load (0.5ms)  SELECT "packages_packages".* FROM "packages_packages" WHERE "packages_packages"."version" IS NOT NULL ORDER BY name ASC
=> EXPLAIN for: SELECT "packages_packages".* FROM "packages_packages" WHERE "packages_packages"."version" IS NOT NULL ORDER BY name ASC
                                          QUERY PLAN
-----------------------------------------------------------------------------------------------
 Index Scan using package_name_index on packages_packages  (cost=0.14..5.64 rows=100 width=71)
   Filter: (version IS NOT NULL)
(2 rows)

📸 Screenshots (strongly suggested)

curl --header "PRIVATE-TOKEN: " "http://gdk.test:3001/api/v4/projects/21/packages"
[
  {
    "id": 101,
    "name": "foo/bar/app/my-maven-package",
    "version": "1.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/101",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/101"
    },
    "created_at": "2020-11-13T17:03:32.174Z",
    "tags": []
  },
  {
    "id": 103,
    "name": "foo/bar/app/my-maven-package",
    "version": "2.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/103",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/103"
    },
    "created_at": "2020-11-13T17:06:18.190Z",
    "tags": []
  },
  {
    "id": 104,
    "name": "foo/bar/app/my-maven-package",
    "version": "3.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/104",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/104"
    },
    "created_at": "2020-11-13T17:08:59.411Z",
    "tags": []
  }
]
curl --header "PRIVATE-TOKEN: " "http://gdk.test:3001/api/v4/projects/21/packages?versionless=true"
[
  {
    "id": 101,
    "name": "foo/bar/app/my-maven-package",
    "version": "1.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/101",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/101"
    },
    "created_at": "2020-11-13T17:03:32.174Z",
    "tags": []
  },
  {
    "id": 102,
    "name": "foo/bar/app/my-maven-package",
    "version": null,
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/102",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/102"
    },
    "created_at": "2020-11-13T17:05:13.699Z",
    "tags": []
  },
  {
    "id": 103,
    "name": "foo/bar/app/my-maven-package",
    "version": "2.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/103",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/103"
    },
    "created_at": "2020-11-13T17:06:18.190Z",
    "tags": []
  },
  {
    "id": 104,
    "name": "foo/bar/app/my-maven-package",
    "version": "3.0-SNAPSHOT",
    "package_type": "maven",
    "_links": {
      "web_path": "/root/maventok/-/packages/104",
      "delete_api_path": "http://gdk.test:3001/api/v4/projects/21/packages/104"
    },
    "created_at": "2020-11-13T17:08:59.411Z",
    "tags": []
  }
]

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • [-] Label as security and @ mention @gitlab-com/gl-security/appsec
  • [-] The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • [-] Security reports checked/validated by a reviewer from the AppSec team
Edited by Steve Abrams

Merge request reports