Add option to view versionless packages in API
🔎 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
-
Changelog entry -
Documentation (if required) -
Code review guidelines -
Merge request performance guidelines -
Style guides - [-] Database guides
- [-] Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. - [-] Tested in all supported browsers
- [-] Informed Infrastructure department of a default or new setting change, if applicable per definition of done
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