Skip to content

Conversation

@XingY
Copy link
Contributor

@XingY XingY commented Sep 10, 2025

Rationale

Allowing sources to have parents in Apps is a relatively new feature, compared with sample's parent. Sample lineage change details are needed for Timeline display, and that's backed by audit event's old/new record map. Sources, however, lacked such detailed audit. A recent refactor of audit added source lineage audit for some scenarios. This PR ensures all insert/update/delete of sources' lineage have proper audit details.

Related Pull Requests

Changes

  • override getExistingRows for dataclassdata to query for existing lineage, used by data iterator
  • update dataclass data getRows to query for exsiting lineage, used by _update
  • refactor experiment/lineage methods to get data parents

@XingY XingY requested a review from Copilot September 19, 2025 17:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds audit detail change functionality for dataclass lineage events by refactoring parent field population logic and improving audit comparison for experimental inputs.

  • Refactored parent field population methods from SampleTypeUpdateServiceDI to ExperimentServiceImpl for reuse
  • Enhanced audit handling to treat parent input values as sets rather than ordered lists
  • Added permission checks and lineage field population for dataclass operations

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
SampleTypeUpdateServiceDI.java Removed duplicate parent field logic and delegated to ExperimentServiceImpl
ExperimentServiceImpl.java Added centralized parent field population methods and updated parent lookup signatures
ExpDataClassDataTableImpl.java Added parent field population and permission checks for dataclass operations
ExperimentService.java Removed deprecated parent lookup method signatures from API
AuditHandler.java Enhanced audit comparison to treat parent inputs as unordered sets

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 177 to 184
if (isExpInput && oldValue != null && newValue != null)
{
// For parent inputs, the order of the values does not matter, so compare as sets
Set<String> oldSet = new HashSet<>(Arrays.asList(oldValue.toString().split(",")));
Set<String> newSet = new HashSet<>(Arrays.asList(newValue.toString().split(",")));
if (oldSet.equals(newSet) && !isExtraAuditField)
continue;
}
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

The split operation on comma-separated values doesn't handle quoted values correctly. Names containing commas are quoted (as seen in line 2555-2556 of ExperimentServiceImpl.java), but this split will break quoted entries. Consider using a CSV parser or the same quoting logic used when creating the values.

Copilot uses AI. Check for mistakes.
Map<String, String> importAliases = _dataClass.getImportAliases();
for (String col : columns)
{
if (ExperimentService.isInputOutputColumn(col) || Strings.CI.equals("parent",col) || importAliases.containsKey(col))
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

The method call Strings.CI.equals("parent",col) has the parameters in an unusual order. Typically the expected value comes first: Strings.CI.equals(col, "parent"). This current order could be confusing and inconsistent with common patterns.

Suggested change
if (ExperimentService.isInputOutputColumn(col) || Strings.CI.equals("parent",col) || importAliases.containsKey(col))
if (ExperimentService.isInputOutputColumn(col) || Strings.CI.equals(col, "parent") || importAliases.containsKey(col))

Copilot uses AI. Check for mistakes.
List<Map<String, Object>> result = new ArrayList<>(keys.size());
for (Map<String, Object> row : keys)
{
Map<String, Object> materialMap = getDataMap(row, user, container, true,false);
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

The boolean parameters true,false are unclear without parameter names. Consider using named parameters or constants to make the intent clear, especially since this pattern appears to indicate allowCrossContainer=true, addInputs=false.

Copilot uses AI. Check for mistakes.
}
else
{
if (isExpInput && oldValue != null && newValue != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

This isExpInput related logic does not seem appropriate for the AuditHandler interface. Is there a way that this can be registered as a part of a current or new audit handler that is only invoked for the necessary tables?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. This has been brought up a few times but hasn't been done due to difficulty. I added a TODO comment as a reminder.

}

protected Map<String, Object> _select(Container container, Long rowId, String lsid, String name, Long classId, boolean allowCrossContainer) throws SQLException
protected Map<String, Object> getDataMap(Map<String, Object> keys, User user, Container container, boolean allowCrossContainer, boolean addInputs) throws SQLException
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Annotate as @Nullable

throw new InvalidKeyException("Value must be supplied for key field 'rowid' or 'lsid' or 'name'", keys);

return _select(container, rowId, lsid, name, classId, allowCrossContainer);
return getDataMap(keys, user, container, allowCrossContainer, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

classId no longer needs to be computed since it is not being handed through.


public void addRowsParentsFields(Set<Identifiable> seeds, Map<Integer, Map<String, Object>> dataRows, User user, Container container)
{
Map<String, Pair<Set<ExpMaterial>, Set<ExpData>>> parents = ExperimentServiceImpl.get().getParentMaterialAndDataMap(container, user, new HashSet<>(seeds));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why new up a HashSet here?

@XingY XingY merged commit ea3ee56 into develop Sep 23, 2025
10 checks passed
@XingY XingY deleted the fb_issue53825 branch September 23, 2025 06:38
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.

4 participants