Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

ProGet 2025.10: License Update API Issues



  • ProGet: 2025.10
    Inedo.Proget: 2.0.5

    The Update License API does not update the database in all cases.

    For example just updating the title via a partial object (which is supposed to be supported according to docs?) never seems to update the database, even though the call always succeeds.

    await _progetClient.UpdateLicenseAsync(new LicenseInfo { Id = 59, Code = "CC-BY-1.0", Title = "Test" });
    

    I've had some success with updating the Urls and PackageNames properties, but even these calls are not always immediately reflected in the database or the UI. Sometimes it feels like the changes only get persisted after clicking around in the UI a bit, but that could just be coincidence. Rarely it is visible directly after the API call.

    I've also tried retrieving the full LicenseInfo object from the database, and using a modified clone of the object with the Update API, but this has also flaky behavior and the title update also never works.

    Am I doing something wrong or is there a glitch in the update API?



  • It appears the AddLicenseAsync() API also has issues:

    • Spdx is sometimes modified, sometimes not set at all
    • PUrl is not set
    • PackageNames is not set

  • inedo-engineer

    Hi @jw ,

    I'm not able to reproduce any issues on my end; I'm not entirely sure how you're testing, but let me share with you the code on the server side in ProGet:

        private static async Task UpdateLicenseAsync(AhHttpContext context, LoggedResponseStream output, WebApiContext apiContext)
        {
            EnsureMethod(context, "POST");
            EnsureCanManageLicenses(apiContext);
    
            var input = await JsonSerializer.DeserializeAsync(context.Request.InputStream, LicenseApiJsonContext.Default.LicenseInfo, context.CancellationToken)
                ?? throw new HttpException(400, "Expected license object.");
    
            var license = await DB.Licenses_GetLicenseAsync(External_Id: input.Code)
                ?? throw new HttpException(404, "License not found.");
    
            List<int>? nameIds = null;
            if (input.PackageNames?.Count > 0)
            {
                nameIds = [];
                foreach (var n in input.PackageNames)
                {
                    if (!PackageNameId.TryParse(n, out var nameId))
                        throw new HttpException(400, $"Invalid package name: {n}");
    
                    nameIds.Add((await nameId.EnsureDatabaseIdAsync()).Id!.Value);
                }
            }
    
            List<int>? versionIds = null;
            if (input.Purls?.Count > 0)
            {
                versionIds = [];
                foreach (var v in input.Purls)
                {
                    if (!PUrl.TryParse(v, out var purl))
                        throw new HttpException(400, $"Invalid purl: {v}");
    
                    versionIds.Add((await ((PackageVersionId)purl).EnsureDatabaseIdAsync()).Id!.Value);
                }
            }
    
            await DB.Licenses_UpdateLicenseDataAsync(
                License_Id: license.License_Id,
                PackageVersionIds_Csv: versionIds?.Count > 0 ? string.Join(',', versionIds) : null,
                PackageNameIds_Csv: nameIds?.Count > 0 ? string.Join(',', nameIds) : null,
                SpdxIds_Csv: input.Spdx?.Count > 0 ? string.Join(',', input.Spdx) : null,
                Urls_Csv: input.Urls?.Count > 0 ? string.Join(',', input.Urls) : null
            );
        }
    

    I'm not sure if that's helpful, but if not... can you put a specific reproduction case?

    Also note that the license data in the UI is cached, but it's invalidated when you visit the /licenses page and others.

    Thanks,
    Steve



  • Hi @stevedennis

    the code seems to explain at least half of the problems I ran into

    • It is not possible to update the Title property of LicenseInfo, it is never passed to the DB
    • It is not possible to update the Code property of LicenseInfo, it is never passed to the DB
    • Partial updates might not be possible, that depends on how DB.Licenses_UpdateLicenseDataAsync handles null values
      At least the code shown here does not differentiate between supplying null or an empty list.
      True partial updates would be something like
      • Null => Do not change the db
      • Empty list => Clear the field in the db
      • Filled list => Overwrite the field in the db

    Given that the properties in the LicenseInfo objects are nullable, the code shown here doesn't quite match the intention of partial updates. At least not how it is described in the docs

    This endpoint supports partial updating by only updating the properties that are supplied in the request.


Log in to reply
 

Inedo Website HomeSupport HomeCode of ConductForums GuideDocumentation