44import org .jetbrains .annotations .NotNull ;
55import org .jetbrains .annotations .Nullable ;
66import org .json .JSONException ;
7+ import org .json .JSONObject ;
78import org .labkey .api .collections .CaseInsensitiveHashMap ;
89import org .labkey .remoteapi .CommandException ;
910import org .labkey .remoteapi .Connection ;
@@ -99,9 +100,11 @@ public enum AuditEvent
99100 PLATE_DATA_AUDIT_EVENT ("PlateDataAuditEvent" ), // available in Biologics module
100101 PLATE_SET_AUDIT_EVENT ("PlateSetEvent" ), // available in Biologics module
101102 QUERY_UPDATE_AUDIT_EVENT ("QueryUpdateAuditEvent" ),
103+ SAMPLE_SET_AUDIT_EVENT ("SampleSetAuditEvent" ),
102104 SAMPLE_TIMELINE_EVENT ("SampleTimelineEvent" ),
103105 SAMPLE_WORKFLOW_AUDIT_EVENT ("SamplesWorkflowAuditEvent" ),
104- SOURCES_AUDIT_EVENT ("SourcesAuditEvent" ); // available with SampleManagement module
106+ SOURCES_AUDIT_EVENT ("SourcesAuditEvent" ), // available with SampleManagement module
107+ TRANSACTION_AUDIT_EVENT ("TransactionAuditEvent" );
105108
106109 private final String _name ;
107110
@@ -116,6 +119,22 @@ public String getName()
116119 }
117120 }
118121
122+ public enum TransactionDetail
123+ {
124+ AuditEvents ,
125+ ImportFileName ,
126+ ClientLibrary ,
127+ Product ,
128+ Action ,
129+ QueryCommand ,
130+ DataIteratorUsed ,
131+ ImportOptions ,
132+ EditMethod ,
133+ RequestSource ,
134+ ETL ,
135+ FileWatcher ;
136+ }
137+
119138 public Integer getLatestAuditRowId (String auditTable ) throws IOException , CommandException
120139 {
121140 String rowId = "rowId" ;
@@ -233,6 +252,49 @@ public void checkAuditEventValuesForTransactionId(String containerPath, AuditEve
233252 }
234253 }
235254
255+ public Map <String , Object > getTransactionAuditLogDetails (Integer transactionAuditId )
256+ {
257+ Connection cn = WebTestHelper .getRemoteApiConnection ();
258+ SelectRowsCommand cmd = new SelectRowsCommand ("auditLog" , "TransactionAuditEvent" );
259+ cmd .setRequiredVersion (9.1 );
260+ cmd .setColumns (Arrays .asList ("TransactionDetails" ));
261+ cmd .addFilter ("RowId" , transactionAuditId , Filter .Operator .EQUAL );
262+ cmd .setContainerFilter (ContainerFilter .AllFolders );
263+
264+ Map <String , Object > event = executeSelectCommand (cn , cmd ).get (0 );
265+ String detailJSON = getLogColumnValue (event , "TransactionDetails" );
266+ log ("TransactionAuditEvent Details: " + detailJSON );
267+ if (detailJSON == null || detailJSON .isEmpty ())
268+ return Collections .emptyMap ();
269+
270+ return new JSONObject (detailJSON ).toMap ();
271+ }
272+
273+ public void checkLastTransactionAuditLogDetails (String containerPath , Map <TransactionDetail , Object > expectedDetails )
274+ {
275+ Integer transactionAuditId = getLastTransactionId (containerPath );
276+ if (transactionAuditId == null )
277+ fail ("No TransactionAuditEvent found in container: " + containerPath );
278+ checkTransactionAuditLogDetails (transactionAuditId , expectedDetails );
279+ }
280+
281+ public void checkTransactionAuditLogDetails (Integer transactionAuditId , Map <TransactionDetail , Object > expectedDetails )
282+ {
283+ Map <String , Object > actualDetails = getTransactionAuditLogDetails (transactionAuditId );
284+ assertEquals ("Unexpected number of events for transactionId " + transactionAuditId , expectedDetails .size (), actualDetails .size ());
285+ for (TransactionDetail key : expectedDetails .keySet ())
286+ {
287+ assertTrue ("Expected detail key not found: " + key , actualDetails .containsKey (key .name ()));
288+ if (TransactionDetail .RequestSource .name ().equals (key .name ()))
289+ {
290+ String expectedValue = expectedDetails .get (key ).toString ();
291+ String actualValue = actualDetails .get (key .name ()) != null ? actualDetails .get (key .name ()).toString () : null ;
292+ assertTrue ("Detail value for key " + key + " not as expected" , actualValue != null && actualValue .contains (expectedValue ));
293+ }
294+ else
295+ assertEquals ("Detail value for key " + key + " not as expected" , expectedDetails .get (key ), actualDetails .get (key .name ()));
296+ }
297+ }
236298 /**
237299 * Check the number of diffs in the audit event. This is a helper function to check the number of diffs in the
238300 * newRecordMap for an audit entry. If a transactionId is provided, it will check all rows for that
@@ -308,6 +370,19 @@ public Integer getLastTransactionId(String containerPath, AuditEvent auditEventN
308370 }
309371 }
310372
373+ public Integer getLastTransactionId (String containerPath )
374+ {
375+ try
376+ {
377+ List <Map <String , Object >> events = getAuditLogsFromLKS (containerPath , AuditEvent .TRANSACTION_AUDIT_EVENT , List .of ("RowId" ), Collections .emptyList (), 1 , ContainerFilter .CurrentAndSubfolders ).getRows ();
378+ return events .size () == 1 ? (Integer ) events .get (0 ).get ("RowId" ) : null ;
379+ }
380+ catch (Exception e )
381+ {
382+ throw new RuntimeException (e );
383+ }
384+ }
385+
311386 public Integer getLastEventId (String containerPath , AuditEvent auditEventName )
312387 {
313388 try
0 commit comments