Skip to content

Add detailed auditing of domain changes & comment ability#6655

Merged
XingY merged 39 commits intodevelopfrom
fb_domainAudit
May 24, 2025
Merged

Add detailed auditing of domain changes & comment ability#6655
XingY merged 39 commits intodevelopfrom
fb_domainAudit

Conversation

@XingY
Copy link
Contributor

@XingY XingY commented May 12, 2025

Rationale

There is currently an App only setting that "requires users provide a reason before completing certain actions", which forces user to enter an audit comment before update/delete a data in the App. Certain update and delete actions of data are wired up to accept auditUserComment but so far no enforcement is in place to ensure this comment is provided. This PR extends the actions that accept auditUserComment to updateDomain, deleteDomain and assay protocol update actions. There is a future story to enforce "required" comment.

This PR also records more details about what's updated during a domain create/update. The domain event and domain property event will now have newRecordMap and oldRecordMap columns, that record all domain or property settings during update/create. Pomain property event will now report more changes, including regex, conditional formatting, text choices, etc.

Related Pull Requests

Changes

  • wire up auditUserComment to updateDomain, deleteDomain and assay protocol actions and utils
  • wire up Domain Audit Event for dataset delete action
  • add old and new record map to Domain and Domain Property Events
  • fix up domain event comments for properties update for sample type, dataclass, assay, list and dataset
  • fix up domain property event comment for column datetime, number format, conditional format, regex validator, text choices update, etc.
  • add selenium tests to check for domain and property audit comment updates

Copy link
Contributor

@labkey-nicka labkey-nicka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, this approach of being very explicit about each change and generating the audit message snippet wherever a changes is made is fine but I don't think it is ideal. The amount of checking for changes and the ease with which future changes could miss adding these details makes this cumbersome and fragile.

Seems like there is potential for a way to have domains retain a difference so that when auditing occurs it can be computed what the auditing should record.

That said, I'm not going to hold up this work on this account.

if (domain == null)
return;

DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(study.getContainer(), String.format("The domain %s was deleted", domain.getName()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you tried to call domain.delete(user, auditUserComment). Why did you move away from that? Did you run into deletion problems / lock-up? Seems that would be much preferred to wiring up our own audit event here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was causing a failure on TC https://teamcity.labkey.org/buildConfiguration/LabkeyTrunk_Bvt/3508494, consistently. I cannot repro the failure locally (the problematic dataset is created in ClientAPITest), nor understand it. It might has to do with study container vs definition container vs domain container, but not sure why no repro for me.

XingY added 8 commits May 14, 2025 13:56
@XingY
Copy link
Contributor Author

XingY commented May 16, 2025

In general, this approach of being very explicit about each change and generating the audit message snippet wherever a changes is made is fine but I don't think it is ideal. The amount of checking for changes and the ease with which future changes could miss adding these details makes this cumbersome and fragile.

Seems like there is potential for a way to have domains retain a difference so that when auditing occurs it can be computed what the auditing should record.

That said, I'm not going to hold up this work on this account.

The implementation has been updated to be a bit more aligned with this comment.

@XingY XingY requested a review from labkey-nicka May 16, 2025 23:35
}

public static String encodeForDataMap(Container c, Map<String, ?> properties)
public static String encodeForDataMap(@Nullable Container c, Map<String, ?> properties)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Looks like the Container parameter is not actually used by this method and can be factored out.


public String toStringVal()
{
if (columnNames == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should this check for empty as well? Seems a bit incongruent to return when null, but then join and display the unique flag setting when columnNames is empty.


@Override
public ValidationException setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List<AnalysisScript> scripts) throws ExperimentException
public Pair<ValidationException, Pair<String, String>> setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List<AnalysisScript> scripts) throws ExperimentException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A typed record would provide more context than type Pair<String, String> about what these strings are.


public Map<String, Object> getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr)
{
Map<String, Object> map = new LinkedHashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include any of the following:

  • getContainer()
  • getPropertyId()
  • getDatabaseDefaultValue()

void save(User user, boolean auditComment) throws ChangePropertyDescriptorException;
void save(User user, @Nullable String allowAddBaseProperty) throws ChangePropertyDescriptorException;
void save(User user, @Nullable Map<String, Object> newRecordMap) throws ChangePropertyDescriptorException;
void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map<String, Object> oldRecordMap, @Nullable Map<String, Object> newRecordMap) throws ChangePropertyDescriptorException;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A comment here about the difference between auditComment and auditUserComment could be useful.


appendValueMapColumns(table);

DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + EVENT_NAME);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider consolidating these URL generation logic:

image

}

@Override
public Map<String, Object> getAuditRecordMap(AssayProvider provider, Container container)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Container parameter is unused

public static List<String> toStringVals(List<GWTIndex> indices, Set<PropertyStorageSpec.Index> excludeBaseIndices)
{
if (indices == null || indices.isEmpty())
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can just return Collections.emptyList() then callers do not need to worry about null values (and could be annotated @NotNull.

return super.toString() + _pd.getPropertyURI();
}

public Map<String, Object> getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since DomainPropertyImpl wraps a PropertyDescriptor should this somehow utilize the audit record of the underlying property descriptor and then extend with whatever additional properties are desired?

@labkey-nicka
Copy link
Contributor

In general, this approach of being very explicit about each change and generating the audit message snippet wherever a changes is made is fine but I don't think it is ideal. The amount of checking for changes and the ease with which future changes could miss adding these details makes this cumbersome and fragile.
Seems like there is potential for a way to have domains retain a difference so that when auditing occurs it can be computed what the auditing should record.
That said, I'm not going to hold up this work on this account.

The implementation has been updated to be a bit more aligned with this comment.

Much improved!

XingY added 10 commits May 21, 2025 00:10
… Pattern and can be hit quickly with unicode characters
# Conflicts:
#	core/package-lock.json
#	core/package.json
#	experiment/package-lock.json
#	experiment/package.json
# Conflicts:
#	biologics/package-lock.json
#	biologics/package.json
#	inventory/package-lock.json
#	inventory/package.json
#	sampleManagement/package-lock.json
#	sampleManagement/package.json
XingY added 4 commits May 23, 2025 12:47
# Conflicts:
#	biologics/package-lock.json
#	biologics/package.json
#	inventory/package-lock.json
#	inventory/package.json
#	sampleManagement/package-lock.json
#	sampleManagement/package.json

Merge branch 'develop' into fb_domainAudit

# Conflicts:
#	core/package-lock.json
#	core/package.json
#	experiment/package-lock.json
#	experiment/package.json
@XingY XingY merged commit 6af0a05 into develop May 24, 2025
6 of 13 checks passed
@XingY XingY deleted the fb_domainAudit branch May 24, 2025 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants