Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# CouchbaseLiteTester
###### version 1.6
###### version 1.7
This app provides a UI to create a local Couchbase Lite DB and Sync Data to the DB from a Couchbase Sync Gateway. It provides features to search for documents in the CBLite DB, selectively sync certain channels and supports both Pull and Push replication.

## Getting Started
For your convenience, I have uploaded a pre-built binary to the [Releases](https://github.com/Infosys/CouchbaseLiteTester/releases) tab. Currently, the binary is tested for Windows only.
For your convenience, I have uploaded a pre-built binary to the [Releases](https://github.com/Infosys/CouchbaseLiteTester/releases) tab.
> Note: Binary releases are provided for major versions. Please build from source for latest features.

Run the Binary by double clicking on the CBLiteTester.jar file or using ``java -jar CBLiteTester.jar``. Java JRE must be correctly installed on the system.
Expand Down Expand Up @@ -70,6 +70,13 @@ Click on Save to save the settings. Then click Reload Table to load the new CBLi
## Creating a Pre-built CBLite Database image for Mobile
The CBLite Tester can also be used to create a pre-built DB image that can be deployed on a mobile app. Simply sync data with any Sync Gateway so that the tool creates a dbname.cblite2 file. Edit documents in the tool as desired and save them. All changes will be saved to the dbname.cblite2 file. Simply copy the database file to your mobile device and all the data in the file should be available in the Couchbase Lite mobile application. Future enhancements to the tool will provide ability to add new documents and attachments (Blobs) to CBLite DB via the tool rather than having to sync from a Sync Gateway.

## Exporting documents from CBLite and importing to Couchbase DB
The CBLite Tester can be used to export documents in CBLite file to a txt file. This file can be imported into Couchbase Server. This can be very handy if there is a Sync issue preventing some docs from syncing. The file can be imported into CB Server using cbimport or Import Documents functionality in the CB web console.
```
cbimport json --format lines -c http://cb-hostname:8091 -u <login> -p <password> -d 'file://cblite_export.txt' -b 'bucket' -g #UUID#
```


## Building the tool from Code
Download from git and import into your IDE of choice.
```
Expand All @@ -88,6 +95,9 @@ mvn compile package
This will create a distributable JAR file in build folder. Package an appropriate defaults.xml file along with your jar file with appropriate environments setup.

## Features
###### version 1.7
* Export docs from CBLite to a txt/json file
* Minor enhancements and bug fixes
###### version 1.6
* Minor enhancements and bug fixes
* Search displays matched doc counts
Expand Down
67 changes: 58 additions & 9 deletions src/main/java/io/amrishraje/cblitetester/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.Duration;
Expand All @@ -52,10 +54,7 @@
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
Expand All @@ -81,6 +80,8 @@ public class MainController implements Initializable {
public TextField tableSearchText;
@FXML
public TextField sgURL;
@FXML
public MenuItem exportDocs;
public AnchorPane tableAnchorPane;
public ToggleSwitch loadFullDocSwitch;
public ToggleSwitch continuousToggle;
Expand Down Expand Up @@ -204,7 +205,7 @@ public void initialize(URL url, ResourceBundle resourceBundle) {
});
replicationMode.setItems(FXCollections.observableArrayList("Pull", "Push", "Pull and Push"));
//Setup About
about.setText("CBLite Tester " + version + " by Amrish Raje" );
about.setText("CBLite Tester " + version + " by Amrish Raje");
}

@FXML
Expand Down Expand Up @@ -251,13 +252,13 @@ public void populateTableV2(boolean fullDoc) {
thread.start();
}

public Task populateTableTask(boolean fullDoc){
public Task populateTableTask(boolean fullDoc) {
return new Task() {
@Override
protected Object call() throws Exception {
try {
SyncController.progressProperty().addListener((observableValue, number, t1) -> {
updateProgress(t1.doubleValue(),1);
updateProgress(t1.doubleValue(), 1);
});
cbLiteDataMap = (SyncController.getDatabase() == null) ? new HashMap<>() : SyncController.getCBLiteData(fullDoc);
} catch (CouchbaseLiteException e) {
Expand Down Expand Up @@ -483,7 +484,7 @@ public void resetSearch(ActionEvent event) {
tableSearchText.clear();
}

public void setupTable(){
public void setupTable() {
docId.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) {
Expand All @@ -502,7 +503,7 @@ public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<Strin
// filteredData = new FilteredList<>(items, p -> true);
tableSearchText.textProperty().addListener((observable, oldValue, newValue) -> {
AtomicInteger docCount = new AtomicInteger();
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),x -> docCountAnchorPane.setVisible(false)));
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5), x -> docCountAnchorPane.setVisible(false)));
filteredData.setPredicate(tableData -> {
// If filter text is empty, display all persons.
if (newValue == null || newValue.isEmpty()) {
Expand Down Expand Up @@ -581,4 +582,52 @@ public void advanceSearch(ActionEvent event) {
logger.error("Error loading AdvanceSearchScreen.fxml", e);
}
}

public void exportDocuments(ActionEvent event) {
logger.info("Clicked Export Docs");
if (!loadFullDocSwitch.isSelected()) {
loadFullDocSwitch.setSelected(true);
loadFullDocument(null);
task.setOnSucceeded(workerStateEvent -> {
progressBar.setVisible(false);
progressText.setVisible(false);
progressAnchorPane.setVisible(false);
try {
writeExcel();
} catch (IOException e) {
logger.error("Cannot export file");
}
});
} else {
try {
writeExcel();
} catch (IOException e) {
logger.error("Cannot export file");
}
}
}

public void writeExcel() throws IOException {
Writer writer = null;
try {
writer = new BufferedWriter(new FileWriter(chooseExportFile()));
StringBuilder exportText = new StringBuilder();
getCbLiteDataMap().forEach((k, v) -> exportText.append(v).append("\n"));
writer.write(exportText.toString());
} catch (Exception ex) {
logger.error("could not open file to export docs");
} finally {
writer.flush();
writer.close();
}
}

public File chooseExportFile() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select export file");
fileChooser.setInitialFileName("cblite_export.txt");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("JSON Files", "*.txt", "*.json"));
return fileChooser.showSaveDialog(null);
}
}
24 changes: 18 additions & 6 deletions src/main/resources/io/amrishraje/cblitetester/CBLiteScreen.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.TableColumn?>
Expand Down Expand Up @@ -82,16 +85,25 @@
</tooltip></TableView>
<AnchorPane fx:id="tableAnchorPane" layoutX="252.0" layoutY="634.0" prefHeight="42.0" prefWidth="650.0" style="-fx-background-color: #888888; -fx-border-color: #777777;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="250.0" AnchorPane.rightAnchor="0.0">
<children>
<TextField fx:id="tableSearchText" layoutX="202.0" layoutY="9.0" prefHeight="26.0" prefWidth="139.0" promptText="Search by Doc Id" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="170.0" />
<Label layoutX="482.0" layoutY="13.0" prefHeight="18.0" prefWidth="118.0" text="Load Full Document" textFill="WHITE" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0">
<TextField fx:id="tableSearchText" layoutX="202.0" layoutY="9.0" prefHeight="26.0" prefWidth="139.0" promptText="Search by Doc Id" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="120.0" />
<Label layoutX="14.0" layoutY="13.0" prefHeight="42.0" prefWidth="63.0" text="Load Full Document" textFill="WHITE" wrapText="true" AnchorPane.bottomAnchor="-3.0" AnchorPane.leftAnchor="13.0">
<tooltip>
<Tooltip text="Loading all documents may take a few moments depending on size of data" />
</tooltip>
</Label>
<ToggleSwitch fx:id="loadFullDocSwitch" layoutX="429.0" layoutY="12.0" onMouseClicked="#loadFullDocument" textFill="WHITE" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="100.0" />
<Button fx:id="addDocButton" layoutX="415.0" layoutY="10.0" mnemonicParsing="false" onAction="#addDocument" text="Add Document" AnchorPane.bottomAnchor="5.0" AnchorPane.rightAnchor="10.0" />
<Button fx:id="resetSearchButton" mnemonicParsing="false" onAction="#resetSearch" text="Reset Search" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="315.0" />
<Button fx:id="advanceSearchButton" layoutX="467.0" layoutY="10.0" mnemonicParsing="false" onAction="#advanceSearch" text="Advance Search..." AnchorPane.bottomAnchor="5.0" AnchorPane.rightAnchor="125.0" />
<ToggleSwitch fx:id="loadFullDocSwitch" layoutX="77.0" layoutY="14.0" onMouseClicked="#loadFullDocument" textFill="WHITE" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="60.0" />
<Button fx:id="addDocButton" layoutX="415.0" layoutY="10.0" mnemonicParsing="false" onAction="#addDocument" text="Add Document" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="470.0" />
<Button fx:id="resetSearchButton" mnemonicParsing="false" onAction="#resetSearch" text="Reset Search" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="265.0" />
<Button fx:id="advanceSearchButton" layoutX="467.0" layoutY="10.0" mnemonicParsing="false" onAction="#advanceSearch" text="Advance Search..." AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="355.0" />
<MenuBar fx:id="menuBarMore" layoutX="579.0" layoutY="11.0" prefHeight="18.0" prefWidth="63.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="578.0">
<menus>
<Menu fx:id="menuMore" mnemonicParsing="false" text="More">
<items>
<MenuItem id="exportDocs" fx:id="exportDocs" mnemonicParsing="false" onAction="#exportDocuments" text="Export Docs" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</AnchorPane>
<AnchorPane fx:id="progressAnchorPane" layoutX="300.0" layoutY="458.0" prefHeight="42.0" prefWidth="400.0" style="-fx-background-color: #999999;" visible="false" AnchorPane.bottomAnchor="100.0" AnchorPane.rightAnchor="150.0">
Expand Down