diff --git a/api/gwtsrc/org/labkey/api/gwt/client/AuditBehaviorType.java b/api/gwtsrc/org/labkey/api/gwt/client/AuditBehaviorType.java index 6fcdec69095..e369374e6d1 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/AuditBehaviorType.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/AuditBehaviorType.java @@ -15,25 +15,43 @@ */ package org.labkey.api.gwt.client; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * User: klum * Date: 10/17/12 */ public enum AuditBehaviorType { - NONE("None"), - DETAILED("Detailed"), - SUMMARY("Summary"); + NONE("None", 1), + DETAILED("Detailed", 3), + SUMMARY("Summary", 2); private final String _label; + private final int _priority; - AuditBehaviorType(String label) + AuditBehaviorType(String label, int priority) { _label = label; + _priority = priority; } public String getLabel() { return _label; } + + public int getPriority() + { + return _priority; + } + + public static AuditBehaviorType getEffectiveAuditLevel(@Nullable AuditBehaviorType apiOverride, @NotNull AuditBehaviorType tableAuditBehaviorType) + { + if (apiOverride == null || apiOverride._priority < tableAuditBehaviorType._priority) + return tableAuditBehaviorType; + + return apiOverride; + } } diff --git a/api/src/org/labkey/api/audit/AbstractAuditHandler.java b/api/src/org/labkey/api/audit/AbstractAuditHandler.java index d34c1bc0a16..d0d89d05b1f 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditHandler.java +++ b/api/src/org/labkey/api/audit/AbstractAuditHandler.java @@ -22,13 +22,19 @@ public abstract class AbstractAuditHandler implements AuditHandler @Override public void addSummaryAuditEvent(User user, Container c, TableInfo table, QueryService.AuditAction action, Integer dataRowCount, @Nullable AuditBehaviorType auditBehaviorType, @Nullable String userComment) + { + addSummaryAuditEvent(user, c, table, action, dataRowCount, auditBehaviorType, userComment, false); + } + + @Override + public void addSummaryAuditEvent(User user, Container c, TableInfo table, QueryService.AuditAction action, Integer dataRowCount, @Nullable AuditBehaviorType auditBehaviorType, @Nullable String userComment, boolean skipAuditLevelCheck) { if (table.supportsAuditTracking()) { AuditConfigurable auditConfigurable = (AuditConfigurable) table; - AuditBehaviorType auditType = auditBehaviorType == null ? auditConfigurable.getAuditBehavior() : auditBehaviorType; + AuditBehaviorType auditType = auditConfigurable.getEffectiveAuditBehavior(auditBehaviorType); - if (auditType == SUMMARY) + if (auditType == SUMMARY || skipAuditLevelCheck) { AuditTypeEvent event = createSummaryAuditRecord(user, c, auditConfigurable, action, userComment, dataRowCount, null); @@ -68,7 +74,7 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud if (table.supportsAuditTracking()) { AuditConfigurable auditConfigurable = (AuditConfigurable)table; - auditType = auditConfigurable.getAuditBehavior(auditType); + auditType = auditConfigurable.getEffectiveAuditBehavior(auditType); // Truncate audit event doesn't accept any params if (action == QueryService.AuditAction.TRUNCATE) @@ -99,6 +105,8 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud AuditTypeEvent event = createSummaryAuditRecord(user, c, auditConfigurable, action, userComment, rows.size(), rows.get(0)); AuditLogService.get().addEvent(user, event); + + return; } case DETAILED: { diff --git a/api/src/org/labkey/api/audit/AuditHandler.java b/api/src/org/labkey/api/audit/AuditHandler.java index 6767eccbdcd..857c0c953f9 100644 --- a/api/src/org/labkey/api/audit/AuditHandler.java +++ b/api/src/org/labkey/api/audit/AuditHandler.java @@ -33,6 +33,11 @@ public interface AuditHandler { void addSummaryAuditEvent(User user, Container c, TableInfo table, QueryService.AuditAction action, Integer dataRowCount, @Nullable AuditBehaviorType auditBehaviorType, @Nullable String userComment); + default void addSummaryAuditEvent(User user, Container c, TableInfo table, QueryService.AuditAction action, Integer dataRowCount, @Nullable AuditBehaviorType auditBehaviorType, @Nullable String userComment, boolean skipAuditLevelCheck) + { + addSummaryAuditEvent(user, c, table, action, dataRowCount, auditBehaviorType, userComment); + } + void addAuditEvent(User user, Container c, TableInfo table, @Nullable AuditBehaviorType auditType, @Nullable String userComment, QueryService.AuditAction action, @Nullable List> rows, @Nullable List> existingRows, boolean useTransactionAuditCache); diff --git a/api/src/org/labkey/api/data/AbstractTableInfo.java b/api/src/org/labkey/api/data/AbstractTableInfo.java index 7b70be7085b..1335a44c86e 100644 --- a/api/src/org/labkey/api/data/AbstractTableInfo.java +++ b/api/src/org/labkey/api/data/AbstractTableInfo.java @@ -280,6 +280,12 @@ public AbstractTableInfo(DbSchema schema, String name) MemTracker.getInstance().put(this); } + @NotNull + @Override + public AuditBehaviorType getDefaultAuditBehavior() + { + return _auditBehaviorType; + } public void afterConstruct() { @@ -1466,7 +1472,6 @@ else if (xmlColumn.isSetWrappedColumnName() && isNotBlank(xmlColumn.getWrappedCo catch (QueryParseException qpe) { warnings.add(qpe.setFieldName(xmlColumn.getColumnName())); - continue; } } @@ -1487,7 +1492,16 @@ else if (!calculatedFieldKeys.isEmpty()) if (xmlTable.getAuditLogging() != null) { AuditType.Enum auditBehavior = xmlTable.getAuditLogging(); - setAuditBehavior(AuditBehaviorType.valueOf(auditBehavior.toString())); + try + { + AuditBehaviorType auditBehaviorType = AuditBehaviorType.valueOf(auditBehavior.toString()); + setAuditBehavior(auditBehaviorType); + } + catch (IllegalArgumentException ignore) + { + warnings.add(new QueryParseWarning("Invalid AuditLogging: " + auditBehavior,null,0,0)); + } + } _warnings.addAll(warnings); @@ -2000,12 +2014,6 @@ public void setAuditBehavior(AuditBehaviorType type) _xmlAuditBehaviorType = type; } - @Override - public AuditBehaviorType getAuditBehavior() - { - return _auditBehaviorType; - } - @Override public AuditBehaviorType getXmlAuditBehaviorType() { diff --git a/api/src/org/labkey/api/data/SchemaTableInfo.java b/api/src/org/labkey/api/data/SchemaTableInfo.java index 8f45396f2f3..d4863150d8d 100644 --- a/api/src/org/labkey/api/data/SchemaTableInfo.java +++ b/api/src/org/labkey/api/data/SchemaTableInfo.java @@ -16,6 +16,7 @@ package org.labkey.api.data; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.cache.CacheManager; @@ -51,6 +52,7 @@ import org.labkey.api.util.StringExpression; import org.labkey.api.util.StringExpressionFactory; import org.labkey.api.util.URLHelper; +import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewContext; import org.labkey.data.xml.AuditType; @@ -112,6 +114,8 @@ public class SchemaTableInfo implements TableInfo, UpdateableTableInfo, AuditCon protected boolean _autoLoadMetaData = true; // TODO: Remove this? DatasetSchemaTableInfo is the only user of this. + private static final Logger LOG = LogHelper.getLogger(SchemaTableInfo.class, "TableInfo construction"); + public SchemaTableInfo(DbSchema parentSchema, DatabaseTableType tableType, String tableName, String metaDataName, SQLFragment selectName) { this(parentSchema, tableType, tableName, metaDataName, selectName, null); @@ -527,14 +531,15 @@ public boolean supportsAuditTracking() } @Override - public void setAuditBehavior(AuditBehaviorType type) + public void setAuditBehavior(@NotNull AuditBehaviorType type) { _auditBehaviorType = type; _xmlAuditBehaviorType = type; } - @Override - public AuditBehaviorType getAuditBehavior() + @Override + @NotNull + public AuditBehaviorType getDefaultAuditBehavior() { return _auditBehaviorType; } @@ -629,7 +634,15 @@ public void loadTablePropertiesFromXml(TableType xmlTable, boolean dontSetName) if (xmlTable.getAuditLogging() != null) { AuditType.Enum auditBehavior = xmlTable.getAuditLogging(); - setAuditBehavior(AuditBehaviorType.valueOf(auditBehavior.toString())); + try + { + AuditBehaviorType auditBehaviorType = AuditBehaviorType.valueOf(auditBehavior.toString()); + setAuditBehavior(auditBehaviorType); + } + catch (IllegalArgumentException ignore) + { + LOG.warn("Invalid AuditLogging: " + auditBehavior); + } } // Stash so we can overlay ColumnInfo properties later diff --git a/api/src/org/labkey/api/data/TableInfo.java b/api/src/org/labkey/api/data/TableInfo.java index 69523006ed4..1671fc34dc4 100644 --- a/api/src/org/labkey/api/data/TableInfo.java +++ b/api/src/org/labkey/api/data/TableInfo.java @@ -161,7 +161,7 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud default AuditHandler getAuditHandler(@Nullable AuditBehaviorType auditBehaviorOverride) { - if (!supportsAuditTracking() || auditBehaviorOverride == AuditBehaviorType.NONE || (auditBehaviorOverride == null && getAuditBehavior() == AuditBehaviorType.NONE)) + if (!supportsAuditTracking() || getEffectiveAuditBehavior(auditBehaviorOverride) == AuditBehaviorType.NONE) return new _DoNothingAuditHandler(); return QueryService.get().getDefaultAuditHandler(); } @@ -662,43 +662,46 @@ void fireRowTrigger(Container c, User user, TriggerType type, boolean before, in Set getAllInvolvedColumns(Collection selectColumns); /** see interface AuditConfigurable */ - default AuditBehaviorType getAuditBehavior() + @NotNull + default AuditBehaviorType getDefaultAuditBehavior() { return AuditBehaviorType.NONE; } + @NotNull + default AuditBehaviorType getEffectiveAuditBehavior() + { + return getXmlAuditBehaviorType() == null ? getDefaultAuditBehavior() : getXmlAuditBehaviorType(); + } + /** * Retrieves the audit behavior for this table taking into account, in order of precedence: - * - the setting from the XML file (always returned if there is a value set) * - the override value provided + * - the setting from the XML file (always returned if there is a value set) * - the value supplied by this table's implementation * - * @param overrideValue value used to override the behavior type provided by the table implementation + * @param apiOverride value used to override the behavior type provided by the table implementation * @return audit behavior for this table */ - default AuditBehaviorType getAuditBehavior(@Nullable AuditBehaviorType overrideValue) + @NotNull + default AuditBehaviorType getEffectiveAuditBehavior(@Nullable AuditBehaviorType apiOverride) { - AuditBehaviorType type = getXmlAuditBehaviorType(); - if (type != null) - return type; - if (overrideValue != null) - return overrideValue; - return getAuditBehavior(); + return AuditBehaviorType.getEffectiveAuditLevel(apiOverride, getEffectiveAuditBehavior()); } - default AuditBehaviorType getAuditBehavior(@Nullable String overrideValue) + default AuditBehaviorType getEffectiveAuditBehavior(@Nullable String apiOverrideValue) { - if (overrideValue != null) + if (apiOverrideValue != null) { try { - return getAuditBehavior(AuditBehaviorType.valueOf(overrideValue)); + return getEffectiveAuditBehavior(AuditBehaviorType.valueOf(apiOverrideValue)); } catch (IllegalArgumentException ignored) { } } - return getAuditBehavior(); + return getEffectiveAuditBehavior(); } /* Can be used to distinguish AuditBehaviorType.NONE vs absent xml audit config */ diff --git a/api/src/org/labkey/api/dataiterator/DetailedAuditLogDataIterator.java b/api/src/org/labkey/api/dataiterator/DetailedAuditLogDataIterator.java index 2c9375f0b33..70678f012b6 100644 --- a/api/src/org/labkey/api/dataiterator/DetailedAuditLogDataIterator.java +++ b/api/src/org/labkey/api/dataiterator/DetailedAuditLogDataIterator.java @@ -70,9 +70,10 @@ protected DetailedAuditLogDataIterator(DataIterator data, DataIteratorContext co _auditAction = auditAction; _auditHandler = table.getAuditHandler(DETAILED); - assert DETAILED == table.getAuditBehavior() || DETAILED == context.getConfigParameter(AuditConfigs.AuditBehavior); + assert DETAILED == table.getEffectiveAuditBehavior((AuditBehaviorType) context.getConfigParameter(AuditConfigs.AuditBehavior)); assert !context.getInsertOption().mergeRows || _data.supportsGetExistingRecord(); assert !context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.BulkLoad); + assert !context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.ByPassAudit); _existingRows = _data.supportsGetExistingRecord() ? new ArrayList<>() : null; } @@ -129,10 +130,10 @@ public static DataIteratorBuilder getDataIteratorBuilder(TableInfo queryTable, @ { AuditBehaviorType auditType = AuditBehaviorType.NONE; if (queryTable.supportsAuditTracking()) - auditType = queryTable.getAuditBehavior((AuditBehaviorType) context.getConfigParameter(AuditConfigs.AuditBehavior)); + auditType = queryTable.getEffectiveAuditBehavior((AuditBehaviorType) context.getConfigParameter(AuditConfigs.AuditBehavior)); // Detailed auditing and not set to bulk load in ETL - if (auditType == DETAILED && !context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.BulkLoad)) + if (auditType == DETAILED && !context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.BulkLoad) && !context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.ByPassAudit)) { DataIterator it = builder.getDataIterator(context); DataIterator in = DataIteratorUtil.wrapMap(it, true); diff --git a/api/src/org/labkey/api/dataiterator/ExistingRecordDataIterator.java b/api/src/org/labkey/api/dataiterator/ExistingRecordDataIterator.java index 333813d0c89..09f0cbac7e8 100644 --- a/api/src/org/labkey/api/dataiterator/ExistingRecordDataIterator.java +++ b/api/src/org/labkey/api/dataiterator/ExistingRecordDataIterator.java @@ -231,7 +231,7 @@ public static DataIteratorBuilder createBuilder(DataIteratorBuilder dib, TableIn { AuditBehaviorType auditType = AuditBehaviorType.NONE; if (target.supportsAuditTracking()) - auditType = target.getAuditBehavior((AuditBehaviorType) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditBehavior)); + auditType = target.getEffectiveAuditBehavior((AuditBehaviorType) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditBehavior)); boolean detailed = auditType == DETAILED; if (useGetRows) return new ExistingDataIteratorsGetRows(new CachingDataIterator(di), target, keys, sharedKeys, context, detailed); diff --git a/api/src/org/labkey/api/exp/query/ExpSchema.java b/api/src/org/labkey/api/exp/query/ExpSchema.java index 2348e8c7ff3..f302e8ab126 100644 --- a/api/src/org/labkey/api/exp/query/ExpSchema.java +++ b/api/src/org/labkey/api/exp/query/ExpSchema.java @@ -440,16 +440,14 @@ public Set getPermittedOps() */ public enum DataClassCategoryType { - registry(null, null), - media(null, null), - sources(AuditBehaviorType.DETAILED, ADDITIONAL_SOURCES_AUDIT_FIELDS); + registry(null), + media(null), + sources(ADDITIONAL_SOURCES_AUDIT_FIELDS); - public final AuditBehaviorType defaultBehavior; public final Set additionalAuditFields; - DataClassCategoryType(@Nullable AuditBehaviorType defaultBehavior, @Nullable Set addlAuditFields) + DataClassCategoryType(@Nullable Set addlAuditFields) { - this.defaultBehavior = defaultBehavior; this.additionalAuditFields = addlAuditFields; } diff --git a/api/src/org/labkey/api/query/AbstractQueryImportAction.java b/api/src/org/labkey/api/query/AbstractQueryImportAction.java index f2add325252..60ad82ff709 100644 --- a/api/src/org/labkey/api/query/AbstractQueryImportAction.java +++ b/api/src/org/labkey/api/query/AbstractQueryImportAction.java @@ -435,9 +435,11 @@ public final ApiResponse _execute(FORM form, BindException errors) throws Except _useAsync = Boolean.valueOf(getParam(Params.useAsync)); // useAsync will save import file to pipeline root as well as run import in a background job + boolean isCrossTypeImport = getOptionParamsMap().getOrDefault(AbstractQueryImportAction.Params.crossTypeImport, false); + // Check first if the audit behavior has been defined for the table either in code or through XML. // If not defined there, check for the audit behavior defined in the action form (getAuditBehaviorType()). - AuditBehaviorType behaviorType = (_target != null) ? _target.getAuditBehavior(getAuditBehaviorType()) : getAuditBehaviorType(); + AuditBehaviorType behaviorType = (_target != null) ? _target.getEffectiveAuditBehavior(getAuditBehaviorType()) : getAuditBehaviorType(); try { @@ -630,8 +632,8 @@ else if (!dataFileDir.exists()) configureLoader(loader); TransactionAuditProvider.TransactionAuditEvent auditEvent = null; - if (behaviorType != null && behaviorType != AuditBehaviorType.NONE) - auditEvent = createTransactionAuditEvent(getContainer(), QueryService.AuditAction.INSERT); + if (isCrossTypeImport || (behaviorType != null && behaviorType != AuditBehaviorType.NONE)) + auditEvent = createTransactionAuditEvent(getContainer(), _insertOption.auditAction); int rowCount = importData(loader, file, originalName, ve, behaviorType, auditEvent, _auditUserComment); diff --git a/api/src/org/labkey/api/query/AbstractQueryUpdateService.java b/api/src/org/labkey/api/query/AbstractQueryUpdateService.java index 72533a62da8..7cd62f644b0 100644 --- a/api/src/org/labkey/api/query/AbstractQueryUpdateService.java +++ b/api/src/org/labkey/api/query/AbstractQueryUpdateService.java @@ -393,9 +393,18 @@ protected int _importRowsUsingDIB(User user, Container container, DataIteratorBu return 0; else { - AuditBehaviorType auditType = (AuditBehaviorType) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditBehavior); - String auditUserComment = (String) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditUserComment); - getQueryTable().getAuditHandler(auditType).addSummaryAuditEvent(user, container, getQueryTable(), context.getInsertOption().auditAction, count, auditType, auditUserComment); + if (!context.isCrossTypeImport() && !context.isCrossFolderImport()) // audit handled at table level + { + AuditBehaviorType auditType = (AuditBehaviorType) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditBehavior); + String auditUserComment = (String) context.getConfigParameter(DetailedAuditLogDataIterator.AuditConfigs.AuditUserComment); + boolean skipAuditLevelCheck = false; + if (context.getConfigParameterBoolean(QueryUpdateService.ConfigParameters.BulkLoad)) + { + if (getQueryTable().getEffectiveAuditBehavior(auditType) == AuditBehaviorType.DETAILED) // allow ETL to demote audit level for bulkLoad + skipAuditLevelCheck = true; + } + getQueryTable().getAuditHandler(auditType).addSummaryAuditEvent(user, container, getQueryTable(), context.getInsertOption().auditAction, count, auditType, auditUserComment, skipAuditLevelCheck); + } return count; } } diff --git a/api/src/org/labkey/api/query/FilteredTable.java b/api/src/org/labkey/api/query/FilteredTable.java index 82c09333aa6..26bdf19b19f 100644 --- a/api/src/org/labkey/api/query/FilteredTable.java +++ b/api/src/org/labkey/api/query/FilteredTable.java @@ -126,7 +126,10 @@ public FilteredTable(@NotNull TableInfo table, @NotNull SchemaType userSchema, @ _importTemplates = _rootTable.getRawImportTemplates(); _buttonBarConfig = BUTTON_BAR_NOT_SET; // lazy copy button bar when asked if (_rootTable.supportsAuditTracking()) - _auditBehaviorType = _rootTable.getAuditBehavior(); + { + _auditBehaviorType = _rootTable.getDefaultAuditBehavior(); + _xmlAuditBehaviorType = _rootTable.getXmlAuditBehaviorType(); + } // We used to copy the titleColumn from table, but this forced all ColumnInfos to load. Now, delegate // to _rootTable lazily, allowing overrides. diff --git a/api/src/org/labkey/api/query/QueryImportPipelineJob.java b/api/src/org/labkey/api/query/QueryImportPipelineJob.java index 59e1c734440..81557cda7be 100644 --- a/api/src/org/labkey/api/query/QueryImportPipelineJob.java +++ b/api/src/org/labkey/api/query/QueryImportPipelineJob.java @@ -296,12 +296,12 @@ public void run() AbstractQueryImportAction.configureLoader(loader, target, _importContextBuilder.getRenamedColumns(), _importContextBuilder.allowLineageColumns(), _importContextBuilder.getLineageImportAliases()); - TransactionAuditProvider.TransactionAuditEvent auditEvent = null; + DataIteratorContext diContext = createDataIteratorContext(ve, getContainer()); - if (_importContextBuilder.getAuditBehaviorType() != null && _importContextBuilder.getAuditBehaviorType() != AuditBehaviorType.NONE) - auditEvent = createTransactionAuditEvent(getContainer(), QueryService.AuditAction.INSERT); + TransactionAuditProvider.TransactionAuditEvent auditEvent = null; + if (diContext.isCrossTypeImport() || (_importContextBuilder.getAuditBehaviorType() != null && _importContextBuilder.getAuditBehaviorType() != AuditBehaviorType.NONE)) + auditEvent = createTransactionAuditEvent(getContainer(), diContext.getInsertOption().auditAction); - DataIteratorContext diContext = createDataIteratorContext(ve, getContainer()); int importedCount = AbstractQueryImportAction.importData(loader, target, updateService, diContext, auditEvent, getInfo().getUser(), getInfo().getContainer()); if (ve.hasErrors()) diff --git a/api/src/org/labkey/api/query/QueryUpdateService.java b/api/src/org/labkey/api/query/QueryUpdateService.java index 1aea04e48fa..02c63825781 100644 --- a/api/src/org/labkey/api/query/QueryUpdateService.java +++ b/api/src/org/labkey/api/query/QueryUpdateService.java @@ -112,7 +112,8 @@ enum ConfigParameters SkipInsertOptionValidation, // (Bool) Skip assert(supportsInsertOption(context.getInsertOption())) for special scenarios (e.g., folder import uses merge action that's otherwise not supported for a table), PreferPKOverObjectUriAsKey, // (Bool) Prefer getPkColumnNames instead of getObjectURIColumnName to use as keys SkipReselectRows, // (Bool) If true, skip qus.getRows and use raw returned rows. Applicable for CommandType.insert/insertWithKeys/update/updateChangingKeys - TargetContainer + TargetContainer, + ByPassAudit // (Bool) If true, skip DetailedAuditLogDataIterator. For internal use only, don't expose for API. } diff --git a/api/src/org/labkey/api/query/UserSchemaAction.java b/api/src/org/labkey/api/query/UserSchemaAction.java index 9bde5af49d1..12f94df02c1 100644 --- a/api/src/org/labkey/api/query/UserSchemaAction.java +++ b/api/src/org/labkey/api/query/UserSchemaAction.java @@ -20,6 +20,7 @@ import org.labkey.api.action.NullSafeBindException; import org.labkey.api.action.SpringActionController; import org.labkey.api.attachments.SpringAttachmentFile; +import org.labkey.api.audit.TransactionAuditProvider; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.collections.CaseInsensitiveMapWrapper; import org.labkey.api.data.ActionButton; @@ -29,6 +30,7 @@ import org.labkey.api.data.DbScope; import org.labkey.api.data.RuntimeSQLException; import org.labkey.api.data.TableInfo; +import org.labkey.api.gwt.client.AuditBehaviorType; import org.labkey.api.security.permissions.InsertPermission; import org.labkey.api.security.permissions.UpdatePermission; import org.labkey.api.util.ButtonBuilder; @@ -249,7 +251,24 @@ else if (table.getPkColumnNames().size() > 1) { try (DbScope.Transaction transaction = dbschema.getScope().ensureTransaction()) { + TransactionAuditProvider.TransactionAuditEvent auditEvent = null; + QueryService.AuditAction auditAction = insert ? QueryService.AuditAction.INSERT : QueryService.AuditAction.UPDATE; + + // transaction audit BatchValidationException batchErrors = new BatchValidationException(); + + AuditBehaviorType auditBehaviorType = table.getEffectiveAuditBehavior(); + if (auditBehaviorType != AuditBehaviorType.NONE) + { + if (transaction.getAuditEvent() != null) + auditEvent = transaction.getAuditEvent(); + else + { + auditEvent = AbstractQueryUpdateService.createTransactionAuditEvent(getContainer(), auditAction); + AbstractQueryUpdateService.addTransactionAuditEvent(transaction, getUser(), auditEvent); + } + } + if (insert) { ret = qus.insertRows(form.getUser(), form.getContainer(), rows, batchErrors, null, null); @@ -280,7 +299,12 @@ else if (table.getPkColumnNames().size() > 1) batchErrors.addToErrors(errors); if (!errors.hasErrors()) + { + if (auditEvent != null) + auditEvent.addComment(auditAction, ret.size()); transaction.commit(); // Only commit if there were no errors + } + } catch (SQLException x) { diff --git a/assay/api-src/org/labkey/api/assay/AssayResultTable.java b/assay/api-src/org/labkey/api/assay/AssayResultTable.java index eac7ab03e07..5e4dc295054 100644 --- a/assay/api-src/org/labkey/api/assay/AssayResultTable.java +++ b/assay/api-src/org/labkey/api/assay/AssayResultTable.java @@ -50,6 +50,7 @@ import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.exp.query.ExpSchema; +import org.labkey.api.gwt.client.AuditBehaviorType; import org.labkey.api.query.AliasedColumn; import org.labkey.api.query.ExprColumn; import org.labkey.api.query.FieldKey; @@ -682,4 +683,12 @@ public boolean supportTableRules() { return super.supportTableRules(); } + + @Override + @NotNull + public AuditBehaviorType getDefaultAuditBehavior() + { + return AuditBehaviorType.DETAILED; + } + } diff --git a/assay/api-src/org/labkey/api/assay/AssayResultUpdateService.java b/assay/api-src/org/labkey/api/assay/AssayResultUpdateService.java index 547ca23a36e..379feab34f1 100644 --- a/assay/api-src/org/labkey/api/assay/AssayResultUpdateService.java +++ b/assay/api-src/org/labkey/api/assay/AssayResultUpdateService.java @@ -16,8 +16,6 @@ package org.labkey.api.assay; import jakarta.servlet.http.HttpServletRequest; -import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.ConvertUtils; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -77,6 +75,8 @@ public class AssayResultUpdateService extends DefaultQueryUpdateService private final AssaySampleLookupContext _assaySampleLookupContext; private final AssayProtocolSchema _schema; + private Map dataChangeCount; + public AssayResultUpdateService(AssayProtocolSchema schema, FilteredTable table) { super(table, table.getRealTable(), createMVMapping(schema.getProvider().getResultsDomain(schema.getProtocol()))); @@ -87,6 +87,28 @@ public AssayResultUpdateService(AssayProtocolSchema schema, FilteredTable table) _schema = schema; } + private void addRunAuditSummary(User user, @Nullable Map configParameters, String verb) + { + String userComment = configParameters == null ? null : (String) configParameters.get(AuditUserComment); + + for (Long runId: dataChangeCount.keySet()) + { + var run = ExperimentService.get().getExpRun(runId); + int deletedCount = dataChangeCount.get(runId); + + ExperimentService.get().auditRunEvent(user, run.getProtocol(), run, null, deletedCount + " data row" + (deletedCount > 1 ? "s have" : " has") + " been " + verb + " in " + run.getProtocol().getName() + ".", userComment); + + } + dataChangeCount = null; + } + + private void incrementAuditRowCount(ExpRun run) + { + long runId = run.getRowId(); + dataChangeCount.putIfAbsent(runId, 0); + dataChangeCount.put(runId, dataChangeCount.get(runId) + 1); + } + @Override public List> updateRows( User user, @@ -98,6 +120,7 @@ public List> updateRows( Map extraScriptContext ) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException { + dataChangeCount = new LinkedHashMap<>(); // handle transform scripts rows = transform(container, user, rows, oldKeys); var result = super.updateRows(user, container, rows, oldKeys, errors, configParameters, extraScriptContext); @@ -107,6 +130,8 @@ public List> updateRows( if (errors.hasErrors()) throw errors; + addRunAuditSummary(user, configParameters, "edited"); + return result; } @@ -265,11 +290,9 @@ protected Map updateRow( Map result = super.updateRow(user, container, row, oldRow, configParameters); Map updatedValues = getRow(user, container, oldRow); - StringBuilder sb = new StringBuilder("Data row, id " + oldRow.get("RowId") + ", edited in " + run.getProtocol().getName() + "."); + TableInfo table = getQueryTable(); for (Map.Entry entry : result.entrySet()) { - // Also check for properties - TableInfo table = getQueryTable(); ColumnInfo col = table.getColumn(entry.getKey()); if (col != null) @@ -280,53 +303,14 @@ protected Map updateRow( if (hasValueChanged) _assaySampleLookupContext.trackSampleLookupChange(container, user, table, col, run); - - TableInfo fkTableInfo = col.getFkTableInfo(); - // Don't follow the lookup for specimen IDs, since their FK is very special and based on target study, etc - if (hasValueChanged && fkTableInfo != null && !AbstractAssayProvider.SPECIMENID_PROPERTY_NAME.equalsIgnoreCase(col.getName())) - { - // Do type conversion in case there's a mismatch in the lookup source and target columns - ColumnInfo fkTablePkCol = fkTableInfo.getPkColumns().get(0); - newValue = lookupDisplayValue(newValue, fkTableInfo, fkTablePkCol); - oldValue = lookupDisplayValue(oldValue, fkTableInfo, fkTablePkCol); - } - appendPropertyIfChanged(sb, col.getLabel(), oldValue, newValue); } } - String userComment = configParameters == null ? null : (String) configParameters.get(AuditUserComment); - ExperimentService.get().auditRunEvent(user, run.getProtocol(), run, null, sb.toString(), userComment); + incrementAuditRowCount(run); return result; } - private Object lookupDisplayValue(Object o, @NotNull TableInfo fkTableInfo, ColumnInfo fkTablePkCol) - { - if (o == null) - return null; - - if (fkTablePkCol == null) - return o; - - if (!fkTablePkCol.getJavaClass().isAssignableFrom(o.getClass())) - { - try - { - o = ConvertUtils.convert(o.toString(), fkTablePkCol.getJavaClass()); - Map newLookupTarget = new TableSelector(fkTableInfo).getMap(o); - if (newLookupTarget != null) - { - o = newLookupTarget.get(fkTableInfo.getTitleColumn()); - } - } - catch (ConversionException ex) - { - // ok - just use the value as is - } - } - - return o; - } private ExpRun getRun(Map row, User user, Class perm) throws InvalidKeyException { @@ -369,8 +353,7 @@ protected Map deleteRow( Map result = super.deleteRow(user, container, oldRowMap); - String userComment = configParameters == null ? null : (String) configParameters.get(AuditUserComment); - ExperimentService.get().auditRunEvent(user, run.getProtocol(), run, null, "Deleted data row, id " + oldRowMap.get("RowId"), userComment); + incrementAuditRowCount(run); if (null != dataObjectMap) { @@ -397,6 +380,7 @@ protected Map deleteRow( @Override public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException { + dataChangeCount = new HashMap<>(); var result = super.deleteRows(user, container, keys, configParameters, extraScriptContext); BatchValidationException errors = new BatchValidationException(); @@ -405,21 +389,9 @@ public List> deleteRows(User user, Container container, List if (errors.hasErrors()) throw errors; - return result; - } + addRunAuditSummary(user, configParameters, "deleted"); - private void appendPropertyIfChanged(StringBuilder sb, String label, Object oldValue, Object newValue) - { - if (Objects.equals(oldValue, newValue)) - return; - - sb.append(" "); - sb.append(label); - sb.append(" changed from "); - sb.append(oldValue == null ? "blank" : "'" + oldValue + "'"); - sb.append(" to "); - sb.append(newValue == null ? "blank" : "'" + newValue + "'"); - sb.append("."); + return result; } /** diff --git a/experiment/src/client/test/integration/MoveSamplesAction.ispec.ts b/experiment/src/client/test/integration/MoveSamplesAction.ispec.ts index 7a2ee57bc98..4b35a760cd3 100644 --- a/experiment/src/client/test/integration/MoveSamplesAction.ispec.ts +++ b/experiment/src/client/test/integration/MoveSamplesAction.ispec.ts @@ -478,13 +478,14 @@ describe('Move Samples', () => { schemaName: 'samples', queryName: SAMPLE_TYPE_NAME_1, rows: [{ rowId: sampleRowId }], + auditBehavior: "NONE", // sample default audit level is DETAILS, api override have no effect }, {...topFolderOptions, ...editorUserOptions}).expect(200); // Assert const {updateCounts} = response.body; expect(updateCounts.samples).toBe(1); expect(updateCounts.sampleAliases).toBe(0); - expect(updateCounts.sampleAuditEvents).toBe(0); + expect(updateCounts.sampleAuditEvents).toBe(1); const sampleExistsInTop = await sampleExists(server, sampleRowId, topFolderOptions, editorUserOptions, SAMPLE_TYPE_NAME_1); expect(sampleExistsInTop).toBe(false); @@ -496,7 +497,9 @@ describe('Move Samples', () => { const sampleEventsInTop = await getSampleTimelineAuditLogs(sampleRowId, topFolderOptions); expect(sampleEventsInTop).toHaveLength(0); const sampleEventsInSub1 = await getSampleTimelineAuditLogs(sampleRowId, subfolder1Options); - expect(sampleEventsInSub1).toHaveLength(0); + expect(sampleEventsInSub1).toHaveLength(2); + expect(caseInsensitive(sampleEventsInSub1[0], 'Comment')).toEqual("Sample folder was updated."); + expect(caseInsensitive(sampleEventsInSub1[1], 'Comment')).toEqual("Sample was registered."); }); it('success, move sample from parent project to subfolder, detailed audit logging', async () => { @@ -567,7 +570,7 @@ describe('Move Samples', () => { schemaName: 'samples', queryName: SAMPLE_TYPE_NAME_1, rows: [{ rowId: sampleRowId }], - auditBehavior: 'SUMMARY', + auditBehavior: 'SUMMARY', // sample default audit level is DETAILS, api override of 'SUMMARY' have no effect auditUserComment: userComment, }, {...topFolderOptions, ...editorUserOptions}).expect(200); @@ -587,8 +590,9 @@ describe('Move Samples', () => { const sampleEventsInTop = await getSampleTimelineAuditLogs(sampleRowId, topFolderOptions); expect(sampleEventsInTop).toHaveLength(0); const sampleEventsInSub1 = await getSampleTimelineAuditLogs(sampleRowId, subfolder1Options); - expect(sampleEventsInSub1).toHaveLength(1); - expect(caseInsensitive(sampleEventsInSub1[0], 'Comment')).toEqual("Sample was registered."); + expect(sampleEventsInSub1).toHaveLength(2); + expect(caseInsensitive(sampleEventsInSub1[0], 'Comment')).toEqual("Sample folder was updated."); + expect(caseInsensitive(sampleEventsInSub1[1], 'Comment')).toEqual("Sample was registered."); }); it('success, move sample from subfolder to parent project', async () => { diff --git a/experiment/src/client/test/integration/MoveSourcesAction.ispec.ts b/experiment/src/client/test/integration/MoveSourcesAction.ispec.ts index 356aeb8f4c9..300b89f33dc 100644 --- a/experiment/src/client/test/integration/MoveSourcesAction.ispec.ts +++ b/experiment/src/client/test/integration/MoveSourcesAction.ispec.ts @@ -469,7 +469,7 @@ describe('Move Sources', () => { expect(success).toBe(true); expect(updateCounts.sources).toBe(1); expect(updateCounts.sourceAliases).toBe(0); - expect(updateCounts.sourceAuditEvents).toBe(0); + expect(updateCounts.sourceAuditEvents).toBe(1); const existsInTop = await _sourceExists(sourceRowId, topFolderOptions); expect(existsInTop).toBe(false); @@ -481,7 +481,9 @@ describe('Move Sources', () => { const eventsInTop = await getDetailedQueryUpdateAuditLogs(sourceRowId, topFolderOptions); expect(eventsInTop).toHaveLength(0); const eventsInSub1 = await getDetailedQueryUpdateAuditLogs(sourceRowId, subfolder1Options); - expect(eventsInSub1).toHaveLength(0); + expect(eventsInSub1).toHaveLength(2); + expect(caseInsensitive(eventsInSub1[0], 'Comment')).toEqual("A row was updated."); + expect(caseInsensitive(eventsInSub1[1], 'Comment')).toEqual("A row was inserted."); }); it('success, move from parent project to subfolder, detailed audit logging', async () => { @@ -554,7 +556,7 @@ describe('Move Sources', () => { schemaName: 'exp.data', queryName: SOURCE_TYPE_NAME_1, rows: [{ RowId: sourceRowId }], - auditBehavior: 'SUMMARY', + auditBehavior: 'SUMMARY', // sample default audit level is DETAILS, api override have no effect auditUserComment: userComment, }, { ...topFolderOptions, ...editorUserOptions }).expect(200); @@ -575,8 +577,9 @@ describe('Move Sources', () => { const eventsInTop = await getDetailedQueryUpdateAuditLogs(sourceRowId, topFolderOptions); expect(eventsInTop).toHaveLength(0); const eventsInSub1 = await getDetailedQueryUpdateAuditLogs(sourceRowId, subfolder1Options); - expect(eventsInSub1).toHaveLength(1); - expect(caseInsensitive(eventsInSub1[0], 'Comment')).toEqual("A row was inserted."); + expect(eventsInSub1).toHaveLength(2); + expect(caseInsensitive(eventsInSub1[0], 'Comment')).toEqual("A row was updated."); + expect(caseInsensitive(eventsInSub1[1], 'Comment')).toEqual("A row was inserted."); }); it('success, move from subfolder to parent project', async () => { diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java index d9f272dc088..6ed1a8eeec0 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java @@ -200,17 +200,10 @@ public Domain getDomain(boolean forUpdate) } @Override - public AuditBehaviorType getAuditBehavior() + @NotNull + public AuditBehaviorType getDefaultAuditBehavior() { - // if there is xml config, use xml config - if (_auditBehaviorType == AuditBehaviorType.NONE && getXmlAuditBehaviorType() == null) - { - ExpSchema.DataClassCategoryType categoryType = ExpSchema.DataClassCategoryType.fromString(_dataClass.getCategory()); - if (categoryType != null && categoryType.defaultBehavior != null) - return categoryType.defaultBehavior; - } - - return _auditBehaviorType; + return AuditBehaviorType.DETAILED; } @Override diff --git a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java index babcdbd03b0..852fcb69578 100644 --- a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java @@ -1665,6 +1665,13 @@ public DataIteratorBuilder persistRows(DataIteratorBuilder data, DataIteratorCon } } + @Override + @NotNull + public AuditBehaviorType getDefaultAuditBehavior() + { + return AuditBehaviorType.DETAILED; + } + static final Set excludeFromDetailedAuditField; static { diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index fd765db6570..2543adf4e48 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -9129,9 +9129,38 @@ public Map> getDomainMetrics() metrics.put("nameexpression", getNameExpressionMetrics()); metrics.put("parentalias", getParentAliasMetrics()); metrics.put("importTemplates", getImportTemplatesMetrics()); + metrics.put("auditLevelOverride", getAuditLevelOverrideMetrics()); return metrics; } + private Map getAuditLevelOverrideMetrics() + { + DbSchema dbSchema = CoreSchema.getInstance().getSchema(); + SQLFragment sql = new SQLFragment("SELECT \"schema\", metadata FROM query.querydef WHERE metadata LIKE '%%'"); + Map[] results = new SqlSelector(dbSchema, sql).getMapArray(); + Map counts = new HashMap<>(); + final String auditNone = "none"; + final String auditSummary = "summary"; + for (Map result : results) + { + String schema = (String) result.get("schema"); + String metadata = (String) result.get("metadata"); + metadata = metadata.toLowerCase(); + if (schema.toLowerCase().startsWith("assay.general.")) + schema = "assay"; + if (schema.equals("assay") || schema.equals("exp.data") || schema.equals("samples")) + { + if (!metadata.contains(auditNone) && !metadata.contains(auditSummary)) + continue; + long count = counts.get(schema) == null ? 0L : counts.get(schema); + counts.put(schema, ++count); + } + } + return Map.of("SampleType", counts.getOrDefault("samples", 0L), + "DataClass", counts.getOrDefault("exp.data", 0L), + "AssayDesign", counts.getOrDefault("assay", 0L)); + } + private Map getImportTemplatesMetrics() { DbSchema dbSchema = CoreSchema.getInstance().getSchema(); @@ -9556,10 +9585,10 @@ public Map moveDataClassObjects(Collection d // create summary audit entries for the source container only. The message is pretty generic, so having it // in both source and target doesn't help much. - addDataClassSummaryAuditEvent(user, sourceContainer, dataClassTable, updateCount, userComment); + QueryService.get().getDefaultAuditHandler().addSummaryAuditEvent(user, sourceContainer, dataClassTable, QueryService.AuditAction.UPDATE, updateCount, AuditBehaviorType.SUMMARY, userComment, true); // create new detailed events for each data object that was moved - AuditBehaviorType dcAuditBehavior = dataClassTable.getAuditBehavior(auditBehavior); + AuditBehaviorType dcAuditBehavior = dataClassTable.getEffectiveAuditBehavior(auditBehavior); if (dcAuditBehavior == AuditBehaviorType.DETAILED) { List> oldRows = new ArrayList<>(); @@ -9594,12 +9623,6 @@ public Map moveDataClassObjects(Collection d return updateCounts; } - private void addDataClassSummaryAuditEvent(User user, Container container, TableInfo dataClassTable, int rowCount, @Nullable String auditUserComment) - { - QueryService queryService = QueryService.get(); - queryService.getDefaultAuditHandler().addSummaryAuditEvent(user, container, dataClassTable, QueryService.AuditAction.UPDATE, rowCount, AuditBehaviorType.SUMMARY, auditUserComment); - } - private void moveDataClassObjectAttachments(ExpDataClass dataClass, Collection classObjects, Container targetContainer, User user) { List attachmentDomainProps = dataClass.getDomain() diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 1c58e46144c..0d874408023 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -103,6 +103,7 @@ import org.labkey.api.query.QueryService; import org.labkey.api.query.SchemaKey; import org.labkey.api.query.SimpleValidationError; +import org.labkey.api.query.UserSchema; import org.labkey.api.query.ValidationException; import org.labkey.api.search.SearchService; import org.labkey.api.security.User; @@ -1161,13 +1162,13 @@ public String getCommentDetailed(QueryService.AuditAction action, boolean isUpda @Override public DetailedAuditTypeEvent createDetailedAuditRecord(User user, Container c, AuditConfigurable tInfo, QueryService.AuditAction action, @Nullable String userComment, @Nullable Map row, Map existingRow) { - return createAuditRecord(c, getCommentDetailed(action, !existingRow.isEmpty()), userComment, action, row, existingRow); + return createAuditRecord(c, tInfo, getCommentDetailed(action, !existingRow.isEmpty()), userComment, action, row, existingRow); } @Override protected AuditTypeEvent createSummaryAuditRecord(User user, Container c, AuditConfigurable tInfo, QueryService.AuditAction action, @Nullable String userComment, int rowCount, @Nullable Map row) { - return createAuditRecord(c, String.format(action.getCommentSummary(), rowCount), userComment, row); + return createAuditRecord(c, tInfo, String.format(action.getCommentSummary(), rowCount), userComment, row); } @Override @@ -1184,9 +1185,9 @@ protected void addDetailedModifiedFields(Map originalRow, Map row) + private SampleTimelineAuditEvent createAuditRecord(Container c, AuditConfigurable tInfo, String comment, String userComment, @Nullable Map row) { - return createAuditRecord(c, comment, userComment, null, row, null); + return createAuditRecord(c, tInfo, comment, userComment, null, row, null); } private boolean isInputFieldKey(String fieldKey) @@ -1196,7 +1197,7 @@ private boolean isInputFieldKey(String fieldKey) slash==ExpMaterial.MATERIAL_INPUT_PARENT.length() && StringUtils.startsWithIgnoreCase(fieldKey,ExpMaterial.MATERIAL_INPUT_PARENT); } - private SampleTimelineAuditEvent createAuditRecord(Container c, String comment, String userComment, @Nullable QueryService.AuditAction action, @Nullable Map row, @Nullable Map existingRow) + private SampleTimelineAuditEvent createAuditRecord(Container c, AuditConfigurable tInfo, String comment, String userComment, @Nullable QueryService.AuditAction action, @Nullable Map row, @Nullable Map existingRow) { SampleTimelineAuditEvent event = new SampleTimelineAuditEvent(c, comment); event.setUserComment(userComment); @@ -1243,6 +1244,19 @@ else if (event.getSampleLsid() != null) // NOTE: to avoid a diff in the audit log make sure row("rowid") is correct! (not the unused generated value) row.put(ROW_ID,staticsRow.get(ROW_ID)); } + else if (tInfo != null) + { + UserSchema schema = tInfo.getUserSchema(); + if (schema != null) + { + ExpSampleType sampleType = getSampleType(c, schema.getUser(), tInfo.getName()); + if (sampleType != null) + { + event.setSampleType(sampleType.getName()); + event.setSampleTypeId(sampleType.getRowId()); + } + } + } if (action != null) { @@ -1895,7 +1909,7 @@ public Map moveSamples(Collection sample int auditEventCount = auditProvider.moveEvents(targetContainer, sampleIds); updateCounts.compute("sampleAuditEvents", (k, c) -> c == null ? auditEventCount : c + auditEventCount ); - AuditBehaviorType stAuditBehavior = samplesTable.getAuditBehavior(auditBehavior); + AuditBehaviorType stAuditBehavior = samplesTable.getEffectiveAuditBehavior(auditBehavior); // create new events for each sample that was moved. if (stAuditBehavior == AuditBehaviorType.DETAILED) { diff --git a/query/src/org/labkey/query/QueryServiceImpl.java b/query/src/org/labkey/query/QueryServiceImpl.java index eb16501c35a..110cd0dc18c 100644 --- a/query/src/org/labkey/query/QueryServiceImpl.java +++ b/query/src/org/labkey/query/QueryServiceImpl.java @@ -53,7 +53,6 @@ import org.labkey.api.data.ConvertHelper; import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbSchemaType; -import org.labkey.api.data.DbScope; import org.labkey.api.data.DisplayColumn; import org.labkey.api.data.Filter; import org.labkey.api.data.ForeignKey; @@ -3202,9 +3201,9 @@ public List getQueryUpdateAuditRecords(User user, Contai { if (table.supportsAuditTracking()) { - AuditBehaviorType auditBehavior = table.getAuditBehavior(); + AuditBehaviorType auditBehavior = table.getEffectiveAuditBehavior(); - if (auditBehavior != null && auditBehavior != AuditBehaviorType.NONE) + if (auditBehavior != AuditBehaviorType.NONE) { return new ActionURL(QueryController.AuditHistoryAction.class, c). addParameter(QueryParam.schemaName, table.getPublicSchemaName()). @@ -3220,7 +3219,7 @@ public DetailsURL getAuditDetailsURL(User user, Container c, TableInfo table) if (table.supportsAuditTracking()) { AuditConfigurable auditConfigurable = (AuditConfigurable)table; - if (auditConfigurable.getAuditBehavior() != AuditBehaviorType.NONE) + if (auditConfigurable.getEffectiveAuditBehavior() != AuditBehaviorType.NONE) { FieldKey rowPk = auditConfigurable.getAuditRowPk(); diff --git a/query/src/org/labkey/query/controllers/QueryController.java b/query/src/org/labkey/query/controllers/QueryController.java index 8c1ae5569e2..c56ee43c32d 100644 --- a/query/src/org/labkey/query/controllers/QueryController.java +++ b/query/src/org/labkey/query/controllers/QueryController.java @@ -4618,7 +4618,7 @@ protected JSONObject executeJson(JSONObject json, CommandType commandType, boole // Check first if the audit behavior has been defined for the table either in code or through XML. // If not defined there, check for the audit behavior defined in the action form (json). - AuditBehaviorType behaviorType = table.getAuditBehavior(json.optString("auditBehavior", null)); + AuditBehaviorType behaviorType = table.getEffectiveAuditBehavior(json.optString("auditBehavior", null)); if (behaviorType != null) { configParameters.put(DetailedAuditLogDataIterator.AuditConfigs.AuditBehavior, behaviorType); diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index 08a39bc4114..9e058748eb7 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -1742,7 +1742,7 @@ public void addSummaryAuditEvent(User user, Container c, TableInfo table, QueryS @Override public void addAuditEvent(User user, Container c, TableInfo table, @Nullable AuditBehaviorType auditTypeOverride, @Nullable String userComment, QueryService.AuditAction action, @Nullable List> rows, @Nullable List> existingRows) { - switch (table.getAuditBehavior(auditTypeOverride)) + switch (table.getEffectiveAuditBehavior(auditTypeOverride)) { case NONE,SUMMARY -> {} case DETAILED -> @@ -1846,7 +1846,7 @@ public void addAuditEvent(User user, Container c, AuditBehaviorType requiredAudi { TableInfo table = _dataset.getTableInfo(user); - if (table != null && table.getAuditBehavior((AuditBehaviorType)null) != requiredAuditType) + if (table != null && table.getEffectiveAuditBehavior((AuditBehaviorType)null) != requiredAuditType) return; DatasetAuditProvider.DatasetAuditEvent event = new DatasetAuditProvider.DatasetAuditEvent(c, comment, _dataset.getDatasetId());