PATCH v1/project with only "active" property sets isLatest to "false"
Current Behavior
I'm sending a PATCH /v1/project/{uuid} request to set the "active" flag to "false". Afterwards, the project's "isLatest" flag is also "false".
This is the request I'm sending:
string requestUri = $"v1/project/{projectId:D}";
var request = new Project
{
Active = false // or true
};
var responseMessage = await client
.PatchAsJsonAsync(requestUri, request, JsonSerializerOptions.Web, cancellationToken)
.ConfigureAwait(false);
This is the definition of the Project type (it's minimal since I don't use a lot of fields at the moment):
class Project
{
public string Uuid { get; set; } = default!;
public string? Name { get; set; }
public string? Version { get; set; }
public bool? IsLatest { get; set; }
public bool? Active { get; set; }
public ProjectClassifier? Classifier { get; set; }
public ExternalReference[]? ExternalReferences { get; set; }
}
As you can see, all properties are nullable, and therefore all of them will be null. Even the "required" attribute Uuid will be null (since that it is the default value for the string type).
I have verified the object has no other properties set than Active.
Steps to Reproduce
- Create a project that's active and latest.
- Use PATCH /v1/project/{uuid} with JSON to set active to false or true.
Expected Behavior
Active is false/true, but IsLatest remains unchanged.
(Information on the environment data below: I'm just a programmer who was tasked to program automations using the API, so I don't know the database server.
Dependency-Track Version
4.13.4
Dependency-Track Distribution
Container Image
Database Server
N/A
Database Server Version
No response
Browser
N/A
Checklist
- [x] I have read and understand the contributing guidelines
- [x] I have checked the existing issues for whether this defect was already reported
Root cause (analysis):
- The Project entity uses primitive boolean fields (active and isLatest) with default values.
- When Jackson deserializes a PATCH body, it creates a Project object and assigns defaults to all boolean fields, even if they’re not sent in the JSON.
- So during the patch merge logic, the code sees active = true and isLatest = false and applies them, even though only one was intended to change.
Proposed solution:
- Create a dedicated ProjectPatchVO (value object) that mirrors the Project fields but uses Boolean instead of boolean.
- When handling PATCH, deserialize the JSON into this VO, then copy only the non-null values into the actual Project entity.
- This way, only the explicitly provided fields get updated.
- Avoids introducing null checks all over the codebase.
- Keeps existing code using Project unchanged (avoids regressions).
Feedback welcome: Would this approach be acceptable from a maintainers’ perspective? If yes, I can open a PR to implement it.
Could it be that the same problem arises with the project's "collection logic" property? Because now that I am using that property, it seems to reset to "NONE" as well.
In general ALL properties must be nullable. Those that are required per API but were null trigger an error response. Those that are not required and null are not to be updated.