From 3d53223e5369fb06c373e5af5431d77036063c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20F=C3=B8lstad?= Date: Tue, 9 Aug 2022 23:08:43 +0200 Subject: [PATCH] Add support for byte string columns. Fixes #141. (#142) * Add support for byte string columns. Fixes #141. * Fixes #145 by upgrading to java 18. --- .github/workflows/build.yaml | 2 +- .../workflows/codacy-coverage-reporter.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- pom.xml | 8 +- .../erikmafo/ltviewer/model/BigtableCell.java | 4 + .../model/BigtableValueConverter.java | 2 + .../model/ByteStringConverterImpl.java | 3 + .../ltviewer/model/CellDefinition.java | 12 ++- .../ltviewer/model/ProtoObjectDefinition.java | 6 +- .../ltviewer/model/ValueTypeConstants.java | 2 + .../internal/testdata/PersonOuterClass.java | 81 ++++++++++++++++++- .../internal/testdata/TestDataUtil.java | 7 +- .../internal/testdata/descriptorset.pb | 7 +- .../services/internal/testdata/person.proto | 1 + .../ltviewer/ui/dialogs/DialogLoaderUtil.java | 53 +++++++----- .../ui/dialogs/TableSettingsDialog.java | 2 +- .../ui/queryresult/cell/CellView.java | 1 - .../util/ByteStringConverterUtil.java | 7 ++ 18 files changed, 157 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4fe5683..1523cd9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -36,7 +36,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Install with maven env: JAVA_TOOL_OPTIONS: ${{ matrix.java-opts }} diff --git a/.github/workflows/codacy-coverage-reporter.yml b/.github/workflows/codacy-coverage-reporter.yml index 566686e..b6c4919 100644 --- a/.github/workflows/codacy-coverage-reporter.yml +++ b/.github/workflows/codacy-coverage-reporter.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Verify with maven env: JAVA_TOOL_OPTIONS: "-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw -Dprism.text=t2k" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 05758c6..5023f35 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1.4.3 with: - java-version: 17 + java-version: 18 - name: Checkout repository uses: actions/checkout@v2 diff --git a/pom.xml b/pom.xml index ab3f5c3..d1a1fd3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.erikmafo littletableviewer - 1.0.1 + 1.0.2 jar Little Table Viewer @@ -39,7 +39,7 @@ maven-compiler-plugin 3.10.1 - 17 + 18 @@ -196,13 +196,13 @@ org.openjfx javafx-controls - 17.0.2 + 18.0.2 compile org.openjfx javafx-fxml - 17.0.2 + 18.0.2 compile diff --git a/src/main/java/com/erikmafo/ltviewer/model/BigtableCell.java b/src/main/java/com/erikmafo/ltviewer/model/BigtableCell.java index 0fce859..9e29d4c 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/BigtableCell.java +++ b/src/main/java/com/erikmafo/ltviewer/model/BigtableCell.java @@ -5,6 +5,8 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.Base64; + /** * A simple representation of a bigtable row cell. */ @@ -70,6 +72,8 @@ public String getQualifier() { */ public String getValueAsString() { return bytes.toStringUtf8(); } + public String getValueAsStringBase64() { return Base64.getEncoder().encodeToString(bytes.toByteArray()); } + /** * Gets the value of the cell as a byte array. * diff --git a/src/main/java/com/erikmafo/ltviewer/model/BigtableValueConverter.java b/src/main/java/com/erikmafo/ltviewer/model/BigtableValueConverter.java index ca4b402..60b9b66 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/BigtableValueConverter.java +++ b/src/main/java/com/erikmafo/ltviewer/model/BigtableValueConverter.java @@ -129,6 +129,8 @@ private BigtableValue convertUsingValueType(BigtableCell cell, @NotNull CellDefi return toBigtableValue(cell.getValueAsString(), valueTypeUpper); case ValueTypeConstants.PROTO: return toBigtableValue(ProtoUtil.toJson(cell.getByteString(), cellDefinition.getProtoObjectDefinition()), valueTypeUpper); + case ValueTypeConstants.BYTE_STRING: + return toBigtableValue(cell.getValueAsStringBase64(), valueTypeUpper); default: return toBigtableValue(cell.getValueAsString(), ValueTypeConstants.STRING); } diff --git a/src/main/java/com/erikmafo/ltviewer/model/ByteStringConverterImpl.java b/src/main/java/com/erikmafo/ltviewer/model/ByteStringConverterImpl.java index d13a379..30c17b9 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/ByteStringConverterImpl.java +++ b/src/main/java/com/erikmafo/ltviewer/model/ByteStringConverterImpl.java @@ -52,6 +52,9 @@ public ByteString toByteString(Field field, Value value) { case ValueTypeConstants.LONG: byteString = ByteStringConverterUtil.toByteString(value.asLong()); break; + case ValueTypeConstants.BYTE_STRING: + byteString = ByteStringConverterUtil.toByteStringFromBase64(value.asString()); + break; default: throw new IllegalArgumentException(String.format("Value type %s is not supported", valueType.toUpperCase())); } diff --git a/src/main/java/com/erikmafo/ltviewer/model/CellDefinition.java b/src/main/java/com/erikmafo/ltviewer/model/CellDefinition.java index 8ec3f3d..4057f6d 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/CellDefinition.java +++ b/src/main/java/com/erikmafo/ltviewer/model/CellDefinition.java @@ -62,6 +62,14 @@ public void setQualifier(String qualifier) { this.qualifier = qualifier; } + public ProtoObjectDefinition getProtoObjectDefinition() { + return protoObjectDefinition; + } + + public void setProtoObjectDefinition(ProtoObjectDefinition protoObjectDefinition) { + this.protoObjectDefinition = protoObjectDefinition; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -85,8 +93,4 @@ public String toString() { ", qualifier='" + qualifier + '\'' + '}'; } - - public ProtoObjectDefinition getProtoObjectDefinition() { - return protoObjectDefinition; - } } diff --git a/src/main/java/com/erikmafo/ltviewer/model/ProtoObjectDefinition.java b/src/main/java/com/erikmafo/ltviewer/model/ProtoObjectDefinition.java index fce833b..84c5845 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/ProtoObjectDefinition.java +++ b/src/main/java/com/erikmafo/ltviewer/model/ProtoObjectDefinition.java @@ -4,9 +4,9 @@ import org.jetbrains.annotations.NotNull; public class ProtoObjectDefinition { - private final String descriptorSetFile; - private final String protoFile; - private final String messageType; + private String descriptorSetFile; + private String protoFile; + private String messageType; public ProtoObjectDefinition(String descriptorSetFile, String protoFile, String messageType) { Check.notNullOrEmpty(descriptorSetFile, "descriptorSetFile"); diff --git a/src/main/java/com/erikmafo/ltviewer/model/ValueTypeConstants.java b/src/main/java/com/erikmafo/ltviewer/model/ValueTypeConstants.java index edabdc7..12d532f 100644 --- a/src/main/java/com/erikmafo/ltviewer/model/ValueTypeConstants.java +++ b/src/main/java/com/erikmafo/ltviewer/model/ValueTypeConstants.java @@ -17,4 +17,6 @@ public class ValueTypeConstants { public static final String JSON = "JSON"; public static final String PROTO = "PROTO"; + + public static final String BYTE_STRING = "BYTESTRING"; } diff --git a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/PersonOuterClass.java b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/PersonOuterClass.java index 08cb4e2..cb193b8 100644 --- a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/PersonOuterClass.java +++ b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/PersonOuterClass.java @@ -46,6 +46,12 @@ public interface PersonOrBuilder extends * @return The age. */ int getAge(); + + /** + * bytes bytes = 4; + * @return The bytes. + */ + com.google.protobuf.ByteString getBytes(); } /** * Protobuf type {@code Person} @@ -62,6 +68,7 @@ private Person(com.google.protobuf.GeneratedMessageV3.Builder builder) { private Person() { name_ = ""; id_ = ""; + bytes_ = com.google.protobuf.ByteString.EMPTY; } @java.lang.Override @@ -111,6 +118,11 @@ private Person( age_ = input.readInt32(); break; } + case 34: { + + bytes_ = input.readBytes(); + break; + } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -232,6 +244,17 @@ public int getAge() { return age_; } + public static final int BYTES_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString bytes_; + /** + * bytes bytes = 4; + * @return The bytes. + */ + @java.lang.Override + public com.google.protobuf.ByteString getBytes() { + return bytes_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -255,6 +278,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (age_ != 0) { output.writeInt32(3, age_); } + if (!bytes_.isEmpty()) { + output.writeBytes(4, bytes_); + } unknownFields.writeTo(output); } @@ -274,6 +300,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeInt32Size(3, age_); } + if (!bytes_.isEmpty()) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, bytes_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -295,6 +325,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getId())) return false; if (getAge() != other.getAge()) return false; + if (!getBytes() + .equals(other.getBytes())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -312,6 +344,8 @@ public int hashCode() { hash = (53 * hash) + getId().hashCode(); hash = (37 * hash) + AGE_FIELD_NUMBER; hash = (53 * hash) + getAge(); + hash = (37 * hash) + BYTES_FIELD_NUMBER; + hash = (53 * hash) + getBytes().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -451,6 +485,8 @@ public Builder clear() { age_ = 0; + bytes_ = com.google.protobuf.ByteString.EMPTY; + return this; } @@ -480,6 +516,7 @@ public PersonOuterClass.Person buildPartial() { result.name_ = name_; result.id_ = id_; result.age_ = age_; + result.bytes_ = bytes_; onBuilt(); return result; } @@ -539,6 +576,9 @@ public Builder mergeFrom(PersonOuterClass.Person other) { if (other.getAge() != 0) { setAge(other.getAge()); } + if (other.getBytes() != com.google.protobuf.ByteString.EMPTY) { + setBytes(other.getBytes()); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -750,6 +790,40 @@ public Builder clearAge() { onChanged(); return this; } + + private com.google.protobuf.ByteString bytes_ = com.google.protobuf.ByteString.EMPTY; + /** + * bytes bytes = 4; + * @return The bytes. + */ + @java.lang.Override + public com.google.protobuf.ByteString getBytes() { + return bytes_; + } + /** + * bytes bytes = 4; + * @param value The bytes to set. + * @return This builder for chaining. + */ + public Builder setBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + + bytes_ = value; + onChanged(); + return this; + } + /** + * bytes bytes = 4; + * @return This builder for chaining. + */ + public Builder clearBytes() { + + bytes_ = getDefaultInstance().getBytes(); + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { @@ -817,8 +891,9 @@ public PersonOuterClass.Person getDefaultInstanceForType() { descriptor; static { java.lang.String[] descriptorData = { - "\n\014person.proto\"/\n\006Person\022\014\n\004name\030\001 \001(\t\022\n" + - "\n\002id\030\002 \001(\t\022\013\n\003age\030\003 \001(\005b\006proto3" + "\n\014person.proto\">\n\006Person\022\014\n\004name\030\001 \001(\t\022\n" + + "\n\002id\030\002 \001(\t\022\013\n\003age\030\003 \001(\005\022\r\n\005bytes\030\004 \001(\014b\006" + + "proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -829,7 +904,7 @@ public PersonOuterClass.Person getDefaultInstanceForType() { internal_static_Person_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_Person_descriptor, - new java.lang.String[] { "Name", "Id", "Age", }); + new java.lang.String[] { "Name", "Id", "Age", "Bytes", }); } // @@protoc_insertion_point(outer_class_scope) diff --git a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/TestDataUtil.java b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/TestDataUtil.java index 52d188b..54fc7e3 100644 --- a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/TestDataUtil.java +++ b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/TestDataUtil.java @@ -1,9 +1,7 @@ package com.erikmafo.ltviewer.services.internal.testdata; import com.erikmafo.ltviewer.model.BigtableInstance; -import com.erikmafo.ltviewer.model.ProtoObjectDefinition; import com.erikmafo.ltviewer.services.internal.BigtableEmulatorSettingsProvider; -import com.erikmafo.ltviewer.util.ProtoUtil; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; import com.google.cloud.bigtable.data.v2.BigtableDataClient; @@ -11,7 +9,6 @@ import com.google.protobuf.ByteString; import org.jetbrains.annotations.NotNull; -import java.io.Console; import java.io.IOException; import static com.erikmafo.ltviewer.util.ByteStringConverterUtil.toByteString; @@ -35,6 +32,7 @@ public class TestDataUtil { private static final String PROJECT_0 = "project-0"; private static final String INSTANCE_0 = "instance-0"; private static final String TABLE_0 = "table-0"; + public static final ByteString BYTE_STRING_TEST_VALUE = toByteString(842098349384L); public static void injectWithTestData(BigtableEmulatorSettingsProvider settingsProvider) { try { @@ -74,7 +72,7 @@ private static void addData(String tableName, com.google.cloud.bigtable.data.v2. .setCell("f1", toByteString("q3"), toByteString(i + 0.5)) .setCell("f1", toByteString("q4"), toByteString(JSON_TEST_DATA)) .setCell("f2", toByteString("q1"), toByteString("string-" + i)) - .setCell("f3", toByteString("q1"), toByteString("string-" + i)) + .setCell("f3", toByteString("q1"), BYTE_STRING_TEST_VALUE) .setCell("f4", toByteString("q1"), getPerson(i).toByteString()); dataClient.mutateRow(mutation); } @@ -87,6 +85,7 @@ private static PersonOuterClass.Person getPerson(int i) { .setName("Person-" + i) .setId("" + i) .setAge(i % 100) + .setBytes(BYTE_STRING_TEST_VALUE) .build(); } } diff --git a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/descriptorset.pb b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/descriptorset.pb index 860357b..a96ea79 100644 --- a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/descriptorset.pb +++ b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/descriptorset.pb @@ -1,7 +1,8 @@ -V - person.proto"> +l + person.proto"T Person name ( Rname id ( Rid -age (Ragebproto3 \ No newline at end of file +age (Rage +bytes ( Rbytesbproto3 \ No newline at end of file diff --git a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/person.proto b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/person.proto index 170a338..f04e8d5 100644 --- a/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/person.proto +++ b/src/main/java/com/erikmafo/ltviewer/services/internal/testdata/person.proto @@ -4,4 +4,5 @@ message Person { string name = 1; string id = 2; int32 age = 3; + bytes bytes = 4; } \ No newline at end of file diff --git a/src/main/java/com/erikmafo/ltviewer/ui/dialogs/DialogLoaderUtil.java b/src/main/java/com/erikmafo/ltviewer/ui/dialogs/DialogLoaderUtil.java index f69f770..8328ee0 100644 --- a/src/main/java/com/erikmafo/ltviewer/ui/dialogs/DialogLoaderUtil.java +++ b/src/main/java/com/erikmafo/ltviewer/ui/dialogs/DialogLoaderUtil.java @@ -14,22 +14,20 @@ public class DialogLoaderUtil { @NotNull public static CompletableFuture displayDialogAndAwaitResult(T initialValue, String fxmlFile) { - CompletableFuture result = new CompletableFuture<>(); - Dialog dialog = new Dialog<>(); - var protoObjectDialogLoader = getLoader(fxmlFile); - DialogPane protoObjectDialog = null; - try { - protoObjectDialog = protoObjectDialogLoader.load(); - } catch (IOException e) { - throw new RuntimeException(e); - } - DialogController controller = protoObjectDialogLoader.getController(); + CompletableFuture completableFuture = new CompletableFuture<>(); + var fxmlLoader = getLoader(fxmlFile); + DialogPane dialogPane = getDialogPane(fxmlLoader); + DialogController controller = getDialogController(initialValue, fxmlLoader); + var dialog = createDialog(completableFuture, dialogPane, controller); + dialog.show(); - if (initialValue != null) { - controller.setResult(initialValue); - } + return completableFuture; + } - dialog.setDialogPane(protoObjectDialog); + @NotNull + private static Dialog createDialog(CompletableFuture completableFuture, DialogPane dialogPane, DialogController controller) { + Dialog dialog = new Dialog<>(); + dialog.setDialogPane(dialogPane); dialog.setResultConverter(param -> { if (ButtonBar.ButtonData.OK_DONE.equals(param.getButtonData())) { return controller.getResult(); @@ -37,14 +35,31 @@ public static CompletableFuture displayDialogAndAwaitResult(T initialValu return null; }); dialog.setOnHidden(ignore -> { - var resultValue = dialog.getResult(); - if (controller.validateResult(resultValue)) { - result.complete(resultValue); + var resultValue = controller.getResult(); + if (resultValue == null || controller.validateResult(resultValue)) { + completableFuture.complete(resultValue); } }); - dialog.show(); + return dialog; + } + + private static DialogController getDialogController(T initialValue, @NotNull FXMLLoader fxmlLoader) { + DialogController controller = fxmlLoader.getController(); + + if (initialValue != null) { + controller.setResult(initialValue); + } + return controller; + } - return result; + private static DialogPane getDialogPane(@NotNull FXMLLoader fxmlLoader) { + DialogPane dialogPane = null; + try { + dialogPane = fxmlLoader.load(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return dialogPane; } @NotNull diff --git a/src/main/java/com/erikmafo/ltviewer/ui/dialogs/TableSettingsDialog.java b/src/main/java/com/erikmafo/ltviewer/ui/dialogs/TableSettingsDialog.java index 23385d8..3401379 100644 --- a/src/main/java/com/erikmafo/ltviewer/ui/dialogs/TableSettingsDialog.java +++ b/src/main/java/com/erikmafo/ltviewer/ui/dialogs/TableSettingsDialog.java @@ -157,7 +157,7 @@ private void addSchemaRow(@NotNull BigtableColumn column, @Nullable String value HBox hbox = new HBox(); ChoiceBox choiceBox = new ChoiceBox<>(); choiceBox.setValue(valueType != null ? valueType : "String"); - choiceBox.getItems().setAll(Arrays.asList("String", "Double", "Float", "Integer", "Long", "Short", "Json", "Proto")); + choiceBox.getItems().setAll(Arrays.asList("String", "Double", "Float", "Integer", "Long", "Short", "Json", "Proto", "ByteString")); choiceBox.setPrefWidth(CHOICE_BOX_PREF_WIDTH); GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); Button configureProtoObjectButton = new Button("", fontAwesome.create(FontAwesome.Glyph.COG)); diff --git a/src/main/java/com/erikmafo/ltviewer/ui/queryresult/cell/CellView.java b/src/main/java/com/erikmafo/ltviewer/ui/queryresult/cell/CellView.java index 32b9541..c9e996f 100644 --- a/src/main/java/com/erikmafo/ltviewer/ui/queryresult/cell/CellView.java +++ b/src/main/java/com/erikmafo/ltviewer/ui/queryresult/cell/CellView.java @@ -11,7 +11,6 @@ import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Label; -import javafx.scene.control.TreeView; import javafx.scene.layout.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/erikmafo/ltviewer/util/ByteStringConverterUtil.java b/src/main/java/com/erikmafo/ltviewer/util/ByteStringConverterUtil.java index f6fc061..5017de0 100644 --- a/src/main/java/com/erikmafo/ltviewer/util/ByteStringConverterUtil.java +++ b/src/main/java/com/erikmafo/ltviewer/util/ByteStringConverterUtil.java @@ -6,6 +6,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Base64; public class ByteStringConverterUtil { @@ -15,6 +16,12 @@ public static ByteString toByteString(String stringUtf8) { return ByteString.copyFromUtf8(stringUtf8); } + @NotNull + @Contract("_ -> new") + public static ByteString toByteStringFromBase64(String stringBase64) { + return ByteString.copyFrom(Base64.getDecoder().decode(stringBase64)); + } + @NotNull public static ByteString toByteString(long value) { var buffer = ByteBuffer