3434import org .labkey .remoteapi .query .DeleteRowsCommand ;
3535import org .labkey .remoteapi .query .Filter ;
3636import org .labkey .remoteapi .query .InsertRowsCommand ;
37+ import org .labkey .remoteapi .query .SaveRowsApiResponse ;
38+ import org .labkey .remoteapi .query .SaveRowsCommand ;
3739import org .labkey .remoteapi .query .SaveRowsResponse ;
3840import org .labkey .remoteapi .query .SelectRowsCommand ;
3941import org .labkey .remoteapi .query .SelectRowsResponse ;
4042import org .labkey .remoteapi .query .Sort ;
4143import org .labkey .remoteapi .query .TruncateTableCommand ;
4244import org .labkey .remoteapi .query .TruncateTableResponse ;
4345import org .labkey .remoteapi .query .UpdateRowsCommand ;
46+ import org .labkey .remoteapi .query .SaveRowsApiCommand ;
47+ import org .labkey .remoteapi .query .SaveRowsApiCommand .Command ;
48+ import org .labkey .remoteapi .query .SaveRowsApiCommand .CommandType ;
4449import org .labkey .remoteapi .security .AddGroupMembersCommand ;
4550import org .labkey .remoteapi .security .CreateGroupCommand ;
4651import org .labkey .remoteapi .security .CreateGroupResponse ;
8489import static org .junit .Assert .fail ;
8590
8691/**
87- * Test for the Java Client API library. This test is written in
88- * Selenium because we don't yet have a way to create a list via
89- * the API, so this test will set up a list and then use the Java
90- * client API library to insert, read, update, and delete from that list
92+ * Tests for the Java Client API library.
9193 */
9294@ Category ({Daily .class })
9395@ BaseWebDriverTest .ClassTimeout (minutes = 4 )
9496public class JavaClientApiTest extends BaseWebDriverTest
9597{
9698 public static final String PROJECT_NAME = JavaClientApiTest .class .getSimpleName () + " Project " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES ;
99+ private static final String SUB_FOLDER_NAME = "Folder " + TRICKY_CHARACTERS_FOR_PROJECT_NAMES ;
100+ private static final String SUB_FOLDER_PATH = PROJECT_NAME + "/" + SUB_FOLDER_NAME ;
97101 public static final String LIST_NAME = "People" + FieldDefinition .DOMAIN_TRICKY_CHARACTERS ;
98102 public static final String LAST_NAME = "LastName" + FieldDefinition .TRICKY_CHARACTERS ;
99103 public static final String USER_NAME = "user1@javaclientapi.test" ;
@@ -113,7 +117,8 @@ public static void doSetup() throws Exception
113117 @ LogMethod
114118 private void setupProject () throws Exception
115119 {
116- _containerHelper .createProject (getProjectName (), null );
120+ _containerHelper .createProject (getProjectName ());
121+ _containerHelper .createSubfolder (getProjectName (), SUB_FOLDER_NAME );
117122
118123 clickProject (PROJECT_NAME );
119124 PortalHelper portalHelper = new PortalHelper (this );
@@ -602,6 +607,173 @@ public void testImpersonationConnection() throws Exception
602607 cn .stopImpersonating ();
603608 }
604609
610+ @ Test
611+ public void testSaveRowsApiCommand () throws Exception
612+ {
613+ // Arrange
614+ String schemaName = "lists" ;
615+ String playersListName = "Players " + FieldDefinition .DOMAIN_TRICKY_CHARACTERS ;
616+ Connection conn = createDefaultConnection ();
617+
618+ String teamsListName = "Teams " + FieldDefinition .DOMAIN_TRICKY_CHARACTERS ;
619+
620+ // Create lists
621+ {
622+ CreateDomainCommand createdListCmd = new CreateDomainCommand ("IntList" , playersListName );
623+ createdListCmd .setOptions (Map .of ("keyName" , "JerseyNumber" , "keyType" , "Integer" ));
624+
625+ Domain domain = createdListCmd .getDomainDesign ();
626+ domain .setFields (List .of (
627+ new PropertyDescriptor ("FirstName" , "First Name" , "string" ),
628+ new PropertyDescriptor ("LastName" , "Last Name" , "string" ),
629+ new PropertyDescriptor ("Team" , null , "string" )
630+ ));
631+
632+ createdListCmd .execute (conn , PROJECT_NAME );
633+
634+ createdListCmd = new CreateDomainCommand ("VarList" , teamsListName );
635+ createdListCmd .setOptions (Map .of ("keyName" , "Team" ));
636+
637+ domain = createdListCmd .getDomainDesign ();
638+ domain .setFields (List .of (
639+ new PropertyDescriptor ("City" , null , "string" ),
640+ new PropertyDescriptor ("Team" , "Team Name" , "string" )
641+ ));
642+
643+ createdListCmd .execute (conn , SUB_FOLDER_PATH );
644+ }
645+
646+ // Add some initial data
647+ {
648+ InsertRowsCommand insertCmd = new InsertRowsCommand (schemaName , playersListName );
649+ insertCmd .addRow (Map .of ("FirstName" , "Alvin" , "LastName" , "David" , "JerseyNumber" , 21 , "Team" , "Seattle Mariners" ));
650+ insertCmd .addRow (Map .of ("FirstName" , "Jay" , "LastName" , "Buhner" , "JerseyNumber" , 19 , "Team" , "New York Yankees" ));
651+ insertCmd .addRow (Map .of ("FirstName" , "Ken" , "LastName" , "Phelps" , "JerseyNumber" , 44 , "Team" , "Seattle Mariners" ));
652+
653+ insertCmd .execute (conn , PROJECT_NAME );
654+
655+ insertCmd = new InsertRowsCommand (schemaName , teamsListName );
656+ insertCmd .addRow (Map .of ("City" , "New York" , "Team" , "Yankees" ));
657+ insertCmd .addRow (Map .of ("City" , "New York" , "Team" , "Giants" ));
658+ insertCmd .addRow (Map .of ("City" , "Brooklyn" , "Team" , "Dodgers" ));
659+ insertCmd .addRow (Map .of ("City" , "Montreal" , "Team" , "Expos" ));
660+ insertCmd .addRow (Map .of ("City" , "Seattle" , "Team" , "Pilots" ));
661+
662+ insertCmd .execute (conn , SUB_FOLDER_PATH );
663+ }
664+
665+ SaveRowsApiCommand saveCmd = new SaveRowsApiCommand ();
666+
667+ // Draft Ken Griffey Jr.
668+ saveCmd .addCommand (new Command (CommandType .Insert , schemaName , playersListName , List .of (
669+ Map .of ("FirstName" , "Ken" , "LastName" , "Griffey Jr." , "JerseyNumber" , 24 , "Team" , "Seattle Mariners" )
670+ )));
671+
672+ // Trade for Jay Buhner
673+ List <Map <String , Object >> rows = List .of (
674+ Map .of ("JerseyNumber" , 19 , "Team" , "Seattle Mariners" ),
675+ Map .of ("JerseyNumber" , 44 , "Team" , "New York Yankees" )
676+ );
677+ Command command = new Command (CommandType .Update , schemaName , playersListName , rows )
678+ .setAuditBehavior (SaveRowsCommand .AuditBehavior .DETAILED )
679+ .setAuditUserComment ("Traded Jay Buhner for Ken Phelps on July 21, 1988" );
680+ saveCmd .addCommand (command );
681+
682+ // Alvin Davis retires
683+ saveCmd .addCommand (new Command (CommandType .Delete , schemaName , playersListName , List .of (Map .of ("JerseyNumber" , 21 ))));
684+
685+ // Teams move west
686+ rows = List .of (
687+ Map .of ("Team" , "Dodgers" , "City" , "Los Angeles" ),
688+ Map .of ("Team" , "Giants" , "City" , "San Francisco" )
689+ );
690+ command = new Command (CommandType .Update , schemaName , teamsListName , rows )
691+ .setContainerPath (SUB_FOLDER_PATH )
692+ .setSkipReselectRows (true );
693+ saveCmd .addCommand (command );
694+
695+ // Some teams are relegated to history
696+ rows = List .of (Map .of ("Team" , "Expos" ), Map .of ("Team" , "Pilots" ));
697+ command = new Command (CommandType .Delete , schemaName , teamsListName , rows )
698+ .setContainerPath (SUB_FOLDER_PATH )
699+ .setAuditBehavior (SaveRowsCommand .AuditBehavior .DETAILED )
700+ .setAuditUserComment ("Expos and Pilots no longer play ball" );
701+ saveCmd .addCommand (command );
702+
703+ // Act
704+ // Execute multiple query operations using a saveRows command
705+ SaveRowsApiResponse response = saveCmd .execute (conn , PROJECT_NAME );
706+
707+ // Assert
708+ assertEquals (200 , response .getStatusCode ());
709+ assertEquals (0 , response .getErrorCount ());
710+ assertEquals (5 , response .getResults ().size ());
711+
712+ // Verify saveRows results per command
713+ {
714+ var result = response .getResults ().get (0 );
715+ assertEquals ("insert" , result .getCommand ());
716+ assertEquals (1 , result .getRowsAffected ());
717+ assertEquals (0 , result .getTransactionAuditId ());
718+
719+ result = response .getResults ().get (1 );
720+ assertEquals ("update" , result .getCommand ());
721+ assertEquals (2 , result .getRowsAffected ());
722+ assertTrue ("Expected a transaction auditId to be provided" , result .getTransactionAuditId () > 0 );
723+
724+ result = response .getResults ().get (2 );
725+ assertEquals ("delete" , result .getCommand ());
726+ assertEquals (1 , result .getRowsAffected ());
727+ assertEquals (0 , result .getTransactionAuditId ());
728+
729+ result = response .getResults ().get (3 );
730+ assertEquals ("update" , result .getCommand ());
731+ assertEquals (2 , result .getRowsAffected ());
732+ assertEquals (0 , result .getTransactionAuditId ());
733+
734+ result = response .getResults ().get (4 );
735+ assertEquals ("delete" , result .getCommand ());
736+ assertEquals (2 , result .getRowsAffected ());
737+ assertTrue ("Expected a transaction auditId to be provided" , result .getTransactionAuditId () > 0 );
738+ }
739+
740+ // Verify players list after operations
741+ {
742+ var selectRowsCommand = new SelectRowsCommand (schemaName , playersListName );
743+ selectRowsCommand .addSort (new Sort ("JerseyNumber" , Sort .Direction .ASCENDING ));
744+
745+ var resp = selectRowsCommand .execute (conn , PROJECT_NAME );
746+ assertEquals (3 , resp .getRowCount ());
747+
748+ var players = resp .getRows ();
749+ assertEquals (19 , players .get (0 ).get ("jerseyNumber" )); // verify case-insensitive
750+ assertEquals ("Seattle Mariners" , players .get (0 ).get ("Team" ));
751+ assertEquals (24 , players .get (1 ).get ("Jerseynumber" )); // verify case-insensitive
752+ assertEquals ("Seattle Mariners" , players .get (1 ).get ("Team" ));
753+ assertEquals (44 , players .get (2 ).get ("JerseyNumber" ));
754+ assertEquals ("New York Yankees" , players .get (2 ).get ("Team" ));
755+ }
756+
757+ // Verify teams list after operations
758+ {
759+ var selectRowsCommand = new SelectRowsCommand (schemaName , teamsListName );
760+ selectRowsCommand .addSort (new Sort ("City" , Sort .Direction .ASCENDING ));
761+
762+ var resp = selectRowsCommand .execute (conn , SUB_FOLDER_PATH );
763+ assertEquals (3 , resp .getRowCount ());
764+
765+ var teams = resp .getRows ();
766+ assertEquals ("Los Angeles" , teams .get (0 ).get ("City" ));
767+ assertEquals ("Dodgers" , teams .get (0 ).get ("Team" ));
768+
769+ assertEquals ("New York" , teams .get (1 ).get ("City" ));
770+ assertEquals ("Yankees" , teams .get (1 ).get ("Team" ));
771+
772+ assertEquals ("San Francisco" , teams .get (2 ).get ("City" ));
773+ assertEquals ("Giants" , teams .get (2 ).get ("Team" ));
774+ }
775+ }
776+
605777 @ Override
606778 protected void doCleanup (boolean afterTest ) throws TestTimeoutException
607779 {
0 commit comments