diff --git a/README.md b/README.md index f4998ee..6925228 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 -p -d 'file://cblite_export.txt' -b 'bucket' -g #UUID# +``` + + ## Building the tool from Code Download from git and import into your IDE of choice. ``` @@ -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 diff --git a/src/main/java/io/amrishraje/cblitetester/MainController.java b/src/main/java/io/amrishraje/cblitetester/MainController.java index 8667b7f..c169fe5 100644 --- a/src/main/java/io/amrishraje/cblitetester/MainController.java +++ b/src/main/java/io/amrishraje/cblitetester/MainController.java @@ -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; @@ -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; @@ -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; @@ -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 @@ -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) { @@ -483,7 +484,7 @@ public void resetSearch(ActionEvent event) { tableSearchText.clear(); } - public void setupTable(){ + public void setupTable() { docId.setCellValueFactory(new Callback, String>, ObservableValue>() { @Override public ObservableValue call(TableColumn.CellDataFeatures, String> p) { @@ -502,7 +503,7 @@ public ObservableValue call(TableColumn.CellDataFeatures(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()) { @@ -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); + } } diff --git a/src/main/resources/io/amrishraje/cblitetester/CBLiteScreen.fxml b/src/main/resources/io/amrishraje/cblitetester/CBLiteScreen.fxml index 7e456c7..afc213f 100644 --- a/src/main/resources/io/amrishraje/cblitetester/CBLiteScreen.fxml +++ b/src/main/resources/io/amrishraje/cblitetester/CBLiteScreen.fxml @@ -4,6 +4,9 @@ + + + @@ -82,16 +85,25 @@ - -