Skip to content

Proposal: Allow release asset links to be replaced while updating a release

Nathan Friend requested to merge nfriend-release-asset-updating into master

What does this MR do?

Proposes adding the ability to replace release asset links as part of the releaseUpdate GraphQL mutation and PUT /projects/:id/releases/:tag_name REST endpoint.

Why?

Our REST API allows a release to be created with asset links in a single request. Our releaseCreate GraphQL mutation does the same.

However, the corresponding REST update endpoint and releaseUpdate GraphQL mutation do not allow any manipulation of links. Instead, to update existing release assets, the user has to use a separate Release Links API. (Note: we don't have a corresponding GraphQL mutation for this yet.)

This is inconvenient for a few reasons:

  1. Updating a release and its assets simultaneously is a common use-case. For example, our own Edit Release page does this. Right now this involves making several requests in sequence:
    1. One request to update the release
    2. One request to update link #1
    3. One request to update link #2
    4. etc...
  2. It's actually not that easy . Because links have unique constraints on both the name and url fields, submitting individual updates can lead to "temporary" validation errors that wouldn't otherwise be an issue if all the updates were accomplished in a single transaction1. Because of this, the only safe way to do this is:
    1. One request to update the release
    2. One request to delete all existing links
    3. One request to recreate link #1
    4. One request to recreate link #2
    5. etc...

This leads to some pretty ugly, error-prone, and non-atomic frontend-code.

It's also a little inconsistent that our current REST and GraphQL APIs allow links to be created with a release, but not updated with a release.

1 For example, if a release has 2 links:

  1. { name: 'Link 1', url: 'https://example.com/1' }
  2. { name: 'Link 2', url: 'https://example.com/2' }
Renaming the first to "Link 2" and the second to "Link 3" would fail, because there would temporarily be two "Link 2"s.

Proposal

Allow both the REST API and GraphQL endpoint to optionally replace existing links with new links in a single request.

In our own codebase, this would effectively move the logic already happening on the frontend (linked above) into the backend, where it can be executed in a single request and within a single transaction.

Why not update instead of replace?

It's tricky to design a bulk-update endpoint that has a nice interface. See https://xuorig.medium.com/graphql-mutation-design-batch-updates-ca2452f92833. We could consider something like this, but it would be more work and would result in a more complex API interface.

Example query/response

This MR shows an example of how we might implement this in GraphQL.

Query
mutation ($updateInput: ReleaseUpdateInput!) {
  releaseUpdate(input: $updateInput) {
    release {
      assets {
        links {
          nodes {
            id
            name
            url
            linkType
            external
            directAssetUrl
          }
        }
      }
    }
    errors
  }
}
Variables
{
  "updateInput": {
    "projectPath": "root/release-test",
    "tagName": "v7.0.5",
    "assets": {
      "links": [{
        "name": "Example link",
        "url": "https://example.com"
      }]
    }
  }
}
Response
{
  "data": {
    "releaseUpdate": {
      "release": {
        "assets": {
          "links": {
            "nodes": [
              {
                "id": "gid://gitlab/Releases::Link/172",
                "name": "Example link",
                "url": "https://example.com",
                "linkType": "OTHER",
                "external": true,
                "directAssetUrl": "https://example.com"
              }
            ]
          }
        }
      },
      "errors": []
    }
  }
}

Other scenarios

All links can be remove by supplying an empty links array:

  • "assets": { "links": [] }

In all of these cases, the release's existing links are unmodified:

  • "assets": { "links": null }
  • "assets": { }
  • "assets": null
  • (no assets property provided)
Edited by Nathan Friend

Merge request reports