From 1fa1f9811787cacec677fda77d731250c2899742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannik=20L=C3=B6scher?= Date: Tue, 24 Mar 2026 17:34:47 +0100 Subject: [PATCH 1/2] AP-23709: Fix deprecated box auth settings using CredentialsFlowVarChoicesProvider AP-23709 (Support username and password fields instead of flow variables in OAuth2 Authenticator nodes) --- .../META-INF/MANIFEST.MF | 4 +- ...tor.node.BoxAuthenticatorSettingsTest.snap | 106 ++++++++++++++---- org.knime.ext.box.authenticator.tests/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../authenticator/node/BoxAppSettings.java | 62 ++++++---- .../node/BoxAuthenticatorSettings.java | 1 + 6 files changed, 134 insertions(+), 43 deletions(-) diff --git a/org.knime.ext.box.authenticator.tests/META-INF/MANIFEST.MF b/org.knime.ext.box.authenticator.tests/META-INF/MANIFEST.MF index 076d8e50..d18728fd 100644 --- a/org.knime.ext.box.authenticator.tests/META-INF/MANIFEST.MF +++ b/org.knime.ext.box.authenticator.tests/META-INF/MANIFEST.MF @@ -2,10 +2,10 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Unit-Tests for org.knime.ext.box.authenticator Bundle-SymbolicName: org.knime.ext.box.authenticator.tests;singleton:=true -Bundle-Version: 5.9.0.qualifier +Bundle-Version: 5.12.0.qualifier Bundle-Vendor: KNIME AG, Zurich, Switzerland Bundle-RequiredExecutionEnvironment: JavaSE-17 -Fragment-Host: org.knime.ext.box.authenticator;bundle-version="[5.6.0,6.0.0)" +Fragment-Host: org.knime.ext.box.authenticator;bundle-version="[5.12.0,6.0.0)" Eclipse-BundleShape: dir Automatic-Module-Name: org.knime.ext.box.authenticator.tests Require-Bundle: org.knime.core.ui.testing;bundle-version="[5.6.0,6.0.0)", diff --git a/org.knime.ext.box.authenticator.tests/files/test_snapshots/org.knime.ext.box.authenticator.node.BoxAuthenticatorSettingsTest.snap b/org.knime.ext.box.authenticator.tests/files/test_snapshots/org.knime.ext.box.authenticator.node.BoxAuthenticatorSettingsTest.snap index 17433db6..3dbc59aa 100644 --- a/org.knime.ext.box.authenticator.tests/files/test_snapshots/org.knime.ext.box.authenticator.node.BoxAuthenticatorSettingsTest.snap +++ b/org.knime.ext.box.authenticator.tests/files/test_snapshots/org.knime.ext.box.authenticator.node.BoxAuthenticatorSettingsTest.snap @@ -1,7 +1,15 @@ { "data" : { "model" : { - "boxApp" : { }, + "boxApp" : { + "secret" : { + "credentials" : { + "isHiddenPassword" : false, + "isHiddenSecondFactor" : false, + "username" : "" + } + } + }, "authType" : "OAUTH", "redirectUrl" : "http://localhost:33749/" } @@ -27,13 +35,55 @@ "boxApp" : { "type" : "object", "properties" : { - "flowVariable" : { - "type" : "string", - "title" : "App ID and secret (flow variable)", - "description" : "Specifies a credentials flow variable with the app/client ID and secret of the custom Box app.\nThese fields can be found in the configuration settings of your custom Box app.\n" + "secret" : { + "type" : "object", + "properties" : { + "credentials" : { + "type" : "object", + "properties" : { + "password" : { + "type" : "string", + "default" : "" + }, + "secondFactor" : { + "type" : "string", + "default" : "" + }, + "username" : { + "type" : "string", + "default" : "" + } + }, + "default" : { + "isHiddenPassword" : false, + "isHiddenSecondFactor" : false, + "username" : "" + } + }, + "flowVarName" : { + "type" : "string" + } + }, + "title" : "App ID and secret", + "description" : "Specifies the app/client ID and secret of the custom Box app.\nThese fields can be found in the configuration settings of your custom Box app.\n", + "default" : { + "credentials" : { + "isHiddenPassword" : false, + "isHiddenSecondFactor" : false, + "username" : "" + } + } } }, - "default" : { } + "default" : { + "secret" : { + "credentials" : { + "isHiddenPassword" : false, + "isHiddenSecondFactor" : false, + "username" : "" + } + } + } }, "enterpriseId" : { "type" : "string", @@ -62,11 +112,12 @@ "type" : "Section", "elements" : [ { "type" : "Control", - "scope" : "#/properties/model/properties/boxApp/properties/flowVariable", + "scope" : "#/properties/model/properties/boxApp/properties/secret", "options" : { - "format" : "dropDown" - }, - "providedOptions" : [ "possibleValues" ] + "passwordLabel" : "Secret", + "usernameLabel" : "ID", + "format" : "legacyCredentials" + } } ] }, { "label" : "Authentication method", @@ -131,10 +182,10 @@ } ], "displayErrorMessage" : true, "showTitleAndDescription" : false, - "dependencies" : [ "#/properties/model/properties/boxApp/properties/flowVariable", "#/properties/model/properties/authType", "#/properties/model/properties/enterpriseId", "#/properties/model/properties/redirectUrl" ], + "dependencies" : [ "#/properties/model/properties/boxApp/properties/secret", "#/properties/model/properties/authType", "#/properties/model/properties/enterpriseId", "#/properties/model/properties/redirectUrl" ], "updateOptions" : { "updateHandler" : "org.knime.ext.box.authenticator.node.BoxAuthenticatorSettings$LoginUpdateHandler", - "dependencies" : [ "#/properties/model/properties/boxApp/properties/flowVariable", "#/properties/model/properties/authType", "#/properties/model/properties/enterpriseId", "#/properties/model/properties/redirectUrl" ] + "dependencies" : [ "#/properties/model/properties/boxApp/properties/secret", "#/properties/model/properties/authType", "#/properties/model/properties/enterpriseId", "#/properties/model/properties/redirectUrl" ] } }, "rule" : { @@ -160,7 +211,18 @@ "boxApp" : { "type" : "object", "properties" : { - "flowVariable" : { } + "secret" : { + "type" : "object", + "properties" : { + "credentials" : { }, + "flowVarName" : { + "configPaths" : [ ] + } + }, + "deprecatedConfigKeys" : [ { + "deprecated" : [ [ "flowVariable" ] ] + } ] + } } }, "authType" : { }, @@ -173,12 +235,16 @@ } } }, - "initialUpdates" : [ { - "scope" : "#/properties/model/properties/boxApp/properties/flowVariable", - "providedOptionName" : "possibleValues", - "values" : [ { - "indices" : [ ], - "value" : [ ] - } ] + "globalUpdates" : [ { + "trigger" : { + "id" : "after-open-dialog" + }, + "triggerInitially" : true, + "dependencies" : [ "#/properties/model/properties/boxApp" ] + }, { + "trigger" : { + "scope" : "#/properties/model/properties/boxApp" + }, + "dependencies" : [ "#/properties/model/properties/boxApp" ] } ] } \ No newline at end of file diff --git a/org.knime.ext.box.authenticator.tests/pom.xml b/org.knime.ext.box.authenticator.tests/pom.xml index 53362e9a..2a9c5e5d 100644 --- a/org.knime.ext.box.authenticator.tests/pom.xml +++ b/org.knime.ext.box.authenticator.tests/pom.xml @@ -15,7 +15,7 @@ eclipse-test-plugin - 5.9.0 + 5.12.0 src/eclipse diff --git a/org.knime.ext.box.authenticator/META-INF/MANIFEST.MF b/org.knime.ext.box.authenticator/META-INF/MANIFEST.MF index cd9934d2..c97acb87 100644 --- a/org.knime.ext.box.authenticator/META-INF/MANIFEST.MF +++ b/org.knime.ext.box.authenticator/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Plugin for the Box Authenticator node Bundle-SymbolicName: org.knime.ext.box.authenticator;singleton:=true -Bundle-Version: 5.9.0.qualifier +Bundle-Version: 5.12.0.qualifier Bundle-Vendor: KNIME AG, Zurich, Switzerland Require-Bundle: org.knime.workbench.repository;bundle-version="[5.9.0,6.0.0)", org.knime.core;bundle-version="[5.9.0,6.0.0)", diff --git a/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAppSettings.java b/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAppSettings.java index fc3088e7..3ea181de 100644 --- a/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAppSettings.java +++ b/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAppSettings.java @@ -50,46 +50,71 @@ import org.knime.core.node.InvalidSettingsException; import org.knime.core.node.workflow.CredentialsProvider; +import org.knime.core.webui.node.dialog.defaultdialog.setting.credentials.LegacyCredentials; +import org.knime.core.webui.node.dialog.defaultdialog.widget.validation.custom.CustomValidation; +import org.knime.credentials.base.node.CredentialVariableMigration; import org.knime.credentials.base.node.CredentialsSettings; import org.knime.node.parameters.Widget; -import org.knime.node.parameters.widget.choices.ChoicesProvider; +import org.knime.node.parameters.migration.Migration; +import org.knime.node.parameters.updates.ParameterReference; +import org.knime.node.parameters.widget.credentials.Credentials; +import org.knime.node.parameters.widget.credentials.CredentialsWidget; /** * Implementation of {@link CredentialsSettings} to supply the ID and secret of * a confidential OAuth2 app. * + * Any instance must be annotated with a {@code ValueReference} to {@link Ref} + * for the validation to work. + * + * * @author Bjoern Lohrmann, KNIME GmbH */ @SuppressWarnings("restriction") public class BoxAppSettings implements CredentialsSettings { /** - * The name of the Credentials flow variable. + * The credential */ - @Widget(title = "App ID and secret (flow variable)", // + @Widget(title = "App ID and secret", // description = """ - Specifies a credentials flow variable with the app/client ID and secret of the custom Box app. + Specifies the app/client ID and secret of the custom Box app. These fields can be found in the configuration settings of your custom Box app. """) - @ChoicesProvider(CredentialsFlowVarChoicesProvider.class) - public String m_flowVariable; + @Migration(CredentialVariableMigration.class) + @CustomValidation(BoxCredentialValidator.class) + @CredentialsWidget(usernameLabel = "ID", passwordLabel = "Secret") // NOSONAR not a password + + public LegacyCredentials m_secret = new LegacyCredentials(new Credentials()); + + /** + * Used to reference this class for the validation. + */ + public interface Ref extends ParameterReference { + } + + static final class BoxCredentialValidator extends AbstractCredentialsValidator { + BoxCredentialValidator() { + super(Ref.class); + } + } @Override - public String flowVariableName() { - return m_flowVariable; + public LegacyCredentials getCredential() { + return m_secret; } /** - * If a flow variable has been specified, this method validates that a username - * and password are present in the flow variable. This method should be used - * during the configure phase, to reduce logspam if the credentials flow - * variable is not there yet. + * If a credential has been specified, this method validates that a username and + * password are present. This method should be used during the configure phase, + * to reduce logspam if the credential is not there yet. * * @param credsProvider - * Used to access the flow variable. + * Used to access the credential. * @throws InvalidSettingsException - * when username or password was not present in the flow variable. + * when username or password was not present in the credential. */ + @Override public void validateOnConfigure(final CredentialsProvider credsProvider) throws InvalidSettingsException { if (retrieve(credsProvider).isPresent()) { validateLogin(credsProvider, "Client/App ID is required"); @@ -98,16 +123,15 @@ public void validateOnConfigure(final CredentialsProvider credsProvider) throws } /** - * This method validates both presence and validity of a credentials flow - * variable. This method should be used during the execute phase. + * This method validates both presence and validity of a credential. This method + * should be used during the execute phase. * * @param credsProvider - * Used to access the flow variable. + * Used to access the credential. * @throws InvalidSettingsException - * when flow variable was not present or invalid. + * when credential was not present or invalid. */ public void validateOnExecute(final CredentialsProvider credsProvider) throws InvalidSettingsException { - validateFlowVariable(credsProvider); validateLogin(credsProvider, "Client/App ID is required"); validateSecret(credsProvider, "Client/App secret is required"); } diff --git a/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAuthenticatorSettings.java b/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAuthenticatorSettings.java index 26f3846f..0aa8cdb1 100644 --- a/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAuthenticatorSettings.java +++ b/org.knime.ext.box.authenticator/src-deprecated/org/knime/ext/box/authenticator/node/BoxAuthenticatorSettings.java @@ -136,6 +136,7 @@ public EffectPredicate init(final PredicateInitializer i) { } @Layout(BoxAppSection.class) + @ValueReference(BoxAppSettings.Ref.class) BoxAppSettings m_boxApp = new BoxAppSettings(); @Widget(title = "Type", description = "Authentication method to use.") From cfa9c34efb8ab55ad246635c529bcbd0b6e3475e Mon Sep 17 00:00:00 2001 From: Tim Crundall Date: Wed, 25 Mar 2026 11:20:11 +0100 Subject: [PATCH 2/2] UIEXT-3487: WebUI-Migration Binary Objects to PNGs UIEXT-3487 (WebUI-Migration Binary Objects to PNGs) --- .../BinaryObjectsToPNGsNodeParameters.xml | 40 ++++ ...BinaryObjectsToPNGsNodeParametersTest.snap | 107 +++++++++++ ...oPNGsNodeParametersTest0.settings.xml.snap | 6 + ...inaryObjectsToPNGsNodeParametersTest1.snap | 107 +++++++++++ ...BinaryObjectsToPNGsNodeParametersTest.java | 92 ++++----- .../BinaryObjectsToPNGsNodeFactory.java | 95 +++++++--- .../BinaryObjectsToPNGsNodeFactory.xml | 44 ----- .../BinaryObjectsToPNGsNodeModel.java | 10 + .../BinaryObjectsToPNGsNodeParameters.java | 179 ++++++++++++++++++ 9 files changed, 570 insertions(+), 110 deletions(-) create mode 100644 org.knime.base.filehandling.tests/files/node_settings/BinaryObjectsToPNGsNodeParameters.xml create mode 100644 org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest.snap create mode 100644 org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest0.settings.xml.snap create mode 100644 org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest1.snap rename org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeDialog.java => org.knime.base.filehandling.tests/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParametersTest.java (51%) delete mode 100644 org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.xml create mode 100644 org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParameters.java diff --git a/org.knime.base.filehandling.tests/files/node_settings/BinaryObjectsToPNGsNodeParameters.xml b/org.knime.base.filehandling.tests/files/node_settings/BinaryObjectsToPNGsNodeParameters.xml new file mode 100644 index 00000000..6400bfa7 --- /dev/null +++ b/org.knime.base.filehandling.tests/files/node_settings/BinaryObjectsToPNGsNodeParameters.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest.snap b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest.snap new file mode 100644 index 00000000..4a189e71 --- /dev/null +++ b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest.snap @@ -0,0 +1,107 @@ +{ + "data" : { + "model" : { + "columnSelection" : "", + "outputMode" : "REPLACE", + "newColumnName" : "PNG" + } + }, + "schema" : { + "type" : "object", + "properties" : { + "model" : { + "type" : "object", + "properties" : { + "columnSelection" : { + "type" : "string", + "title" : "Column selection", + "description" : "Column containing the binary objects that will be converted to PNG images.", + "default" : "" + }, + "newColumnName" : { + "type" : "string", + "title" : "New column name", + "description" : "Name of the appended column. Only applicable when \"Append\" is selected.", + "default" : "PNG" + }, + "outputMode" : { + "oneOf" : [ { + "const" : "REPLACE", + "title" : "Replace" + }, { + "const" : "APPEND", + "title" : "Append" + } ], + "title" : "Output handling", + "description" : "Determines whether to append a new PNG column or to replace the selected binary object column.\n
    \n
  • Replace: Replaces the selected binary object column with the PNG column.
  • \n
  • Append: Appends a new PNG column to the table.
  • \n
", + "default" : "REPLACE" + } + } + } + } + }, + "ui_schema" : { + "elements" : [ { + "type" : "Control", + "scope" : "#/properties/model/properties/columnSelection", + "options" : { + "format" : "dropDown" + }, + "providedOptions" : [ "possibleValues" ] + }, { + "type" : "Control", + "scope" : "#/properties/model/properties/outputMode", + "options" : { + "format" : "valueSwitch" + } + }, { + "type" : "Control", + "scope" : "#/properties/model/properties/newColumnName", + "rule" : { + "effect" : "SHOW", + "condition" : { + "scope" : "#/properties/model/properties/outputMode", + "schema" : { + "oneOf" : [ { + "const" : "APPEND" + } ] + } + } + } + } ] + }, + "persist" : { + "type" : "object", + "properties" : { + "model" : { + "type" : "object", + "properties" : { + "columnSelection" : { + "configKey" : "columnselection" + }, + "outputMode" : { + "configPaths" : [ [ "replace" ] ] + }, + "newColumnName" : { + "configKey" : "columnname" + } + } + } + } + }, + "initialUpdates" : [ { + "scope" : "#/properties/model/properties/columnSelection", + "providedOptionName" : "possibleValues", + "values" : [ { + "indices" : [ ], + "value" : [ { + "id" : "foo", + "text" : "foo", + "type" : { + "id" : "org.knime.core.data.blob.BinaryObjectDataValue", + "text" : "Binary Object" + } + } ] + } ] + } ] +} \ No newline at end of file diff --git a/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest0.settings.xml.snap b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest0.settings.xml.snap new file mode 100644 index 00000000..89d4084e --- /dev/null +++ b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest0.settings.xml.snap @@ -0,0 +1,6 @@ + + + + + + diff --git a/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest1.snap b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest1.snap new file mode 100644 index 00000000..3eed69d6 --- /dev/null +++ b/org.knime.base.filehandling.tests/files/test_snapshots/org.knime.base.filehandling.binaryobjectstopngs.BinaryObjectsToPNGsNodeParametersTest1.snap @@ -0,0 +1,107 @@ +{ + "data" : { + "model" : { + "columnSelection" : "Image", + "outputMode" : "APPEND", + "newColumnName" : "PNG" + } + }, + "schema" : { + "type" : "object", + "properties" : { + "model" : { + "type" : "object", + "properties" : { + "columnSelection" : { + "type" : "string", + "title" : "Column selection", + "description" : "Column containing the binary objects that will be converted to PNG images.", + "default" : "" + }, + "newColumnName" : { + "type" : "string", + "title" : "New column name", + "description" : "Name of the appended column. Only applicable when \"Append\" is selected.", + "default" : "PNG" + }, + "outputMode" : { + "oneOf" : [ { + "const" : "REPLACE", + "title" : "Replace" + }, { + "const" : "APPEND", + "title" : "Append" + } ], + "title" : "Output handling", + "description" : "Determines whether to append a new PNG column or to replace the selected binary object column.\n
    \n
  • Replace: Replaces the selected binary object column with the PNG column.
  • \n
  • Append: Appends a new PNG column to the table.
  • \n
", + "default" : "REPLACE" + } + } + } + } + }, + "ui_schema" : { + "elements" : [ { + "type" : "Control", + "scope" : "#/properties/model/properties/columnSelection", + "options" : { + "format" : "dropDown" + }, + "providedOptions" : [ "possibleValues" ] + }, { + "type" : "Control", + "scope" : "#/properties/model/properties/outputMode", + "options" : { + "format" : "valueSwitch" + } + }, { + "type" : "Control", + "scope" : "#/properties/model/properties/newColumnName", + "rule" : { + "effect" : "SHOW", + "condition" : { + "scope" : "#/properties/model/properties/outputMode", + "schema" : { + "oneOf" : [ { + "const" : "APPEND" + } ] + } + } + } + } ] + }, + "persist" : { + "type" : "object", + "properties" : { + "model" : { + "type" : "object", + "properties" : { + "columnSelection" : { + "configKey" : "columnselection" + }, + "outputMode" : { + "configPaths" : [ [ "replace" ] ] + }, + "newColumnName" : { + "configKey" : "columnname" + } + } + } + } + }, + "initialUpdates" : [ { + "scope" : "#/properties/model/properties/columnSelection", + "providedOptionName" : "possibleValues", + "values" : [ { + "indices" : [ ], + "value" : [ { + "id" : "foo", + "text" : "foo", + "type" : { + "id" : "org.knime.core.data.blob.BinaryObjectDataValue", + "text" : "Binary Object" + } + } ] + } ] + } ] +} \ No newline at end of file diff --git a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeDialog.java b/org.knime.base.filehandling.tests/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParametersTest.java similarity index 51% rename from org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeDialog.java rename to org.knime.base.filehandling.tests/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParametersTest.java index d3e9235d..030d102c 100644 --- a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeDialog.java +++ b/org.knime.base.filehandling.tests/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParametersTest.java @@ -1,5 +1,6 @@ /* * ------------------------------------------------------------------------ + * * Copyright by KNIME AG, Zurich, Switzerland * Website: http://www.knime.com; Email: contact@knime.com * @@ -41,61 +42,64 @@ * may freely choose the license terms applicable to such Node, including * when such Node is propagated with or for interoperation with KNIME. * ------------------------------------------------------------------------ - * - * History - * Oct 30, 2012 (Patrick Winter): created */ package org.knime.base.filehandling.binaryobjectstopngs; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; +import java.io.FileInputStream; +import java.io.IOException; -import org.knime.core.data.blob.BinaryObjectDataValue; -import org.knime.core.node.defaultnodesettings.DefaultNodeSettingsPane; -import org.knime.core.node.defaultnodesettings.DialogComponentButtonGroup; -import org.knime.core.node.defaultnodesettings.DialogComponentColumnNameSelection; -import org.knime.core.node.defaultnodesettings.DialogComponentString; -import org.knime.core.node.defaultnodesettings.SettingsModelString; +import org.knime.core.data.DataTableSpec; +import org.knime.core.data.DataType; +import org.knime.core.data.blob.BinaryObjectDataCell; +import org.knime.core.node.InvalidSettingsException; +import org.knime.core.node.NodeSettings; +import org.knime.core.node.port.PortObjectSpec; +import org.knime.core.webui.node.dialog.SettingsType; +import org.knime.core.webui.node.dialog.defaultdialog.NodeParametersUtil; +import org.knime.testing.node.dialog.DefaultNodeSettingsSnapshotTest; +import org.knime.testing.node.dialog.SnapshotTestConfiguration; /** - * NodeDialog for the node. - * - * - * @author Patrick Winter, KNIME AG, Zurich, Switzerland + * Snapshot test for {@link BinaryObjectsToPNGsNodeParameters}. + * + * @author Tim Crundall, TNG Technology Consulting GmbH */ -public class BinaryObjectsToPNGsNodeDialog extends DefaultNodeSettingsPane { +@SuppressWarnings("restriction") +final class BinaryObjectsToPNGsNodeParametersTest extends DefaultNodeSettingsSnapshotTest { - private SettingsModelString m_columnselection; - - private SettingsModelString m_columnname; + BinaryObjectsToPNGsNodeParametersTest() { + super(getConfig()); + } - private SettingsModelString m_replace; + private static SnapshotTestConfiguration getConfig() { + return SnapshotTestConfiguration.builder() // + .withInputPortObjectSpecs(createInputPortSpecs()) // + .testJsonFormsForModel(BinaryObjectsToPNGsNodeParameters.class) // + .testJsonFormsWithInstance(SettingsType.MODEL, () -> readSettings()) // + .testNodeSettingsStructure(() -> readSettings()) // + .build(); + } - /** - * New pane for configuring the node dialog. - */ - @SuppressWarnings("unchecked") - protected BinaryObjectsToPNGsNodeDialog() { - super(); - m_columnselection = SettingsFactory.createColumnSelectionSettings(); - m_replace = SettingsFactory.createReplacePolicySettings(); - m_columnname = SettingsFactory.createColumnNameSettings(m_replace); - m_replace.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(final ChangeEvent e) { - boolean append = m_replace.getStringValue().equals(ReplacePolicy.APPEND.getName()); - m_columnname.setEnabled(append); + private static BinaryObjectsToPNGsNodeParameters readSettings() { + try { + var path = getSnapshotPath(BinaryObjectsToPNGsNodeParameters.class).getParent().resolve("node_settings") + .resolve("BinaryObjectsToPNGsNodeParameters.xml"); + try (var fis = new FileInputStream(path.toFile())) { + var nodeSettings = NodeSettings.loadFromXML(fis); + return NodeParametersUtil.loadSettings(nodeSettings.getNodeSettings(SettingsType.MODEL.getConfigKey()), + BinaryObjectsToPNGsNodeParameters.class); } - }); - // Column selection - addDialogComponent(new DialogComponentColumnNameSelection(m_columnselection, "Column selection", 0, - BinaryObjectDataValue.class)); - createNewGroup("New column..."); - // Replace - addDialogComponent(new DialogComponentButtonGroup(m_replace, false, "", ReplacePolicy.getAllSettings())); - // Column name - addDialogComponent(new DialogComponentString(m_columnname, "Name", true, 20)); - closeCurrentGroup(); + } catch (IOException | InvalidSettingsException e) { + throw new IllegalStateException(e); + } + } + + private static PortObjectSpec[] createInputPortSpecs() { + return new PortObjectSpec[]{createDefaultTestTableSpec()}; } + private static DataTableSpec createDefaultTestTableSpec() { + return new DataTableSpec(new String[]{"foo"}, + new DataType[]{DataType.getType(BinaryObjectDataCell.class), }); + } } diff --git a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.java b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.java index 85cf0994..159d2984 100644 --- a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.java +++ b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.java @@ -41,63 +41,114 @@ * may freely choose the license terms applicable to such Node, including * when such Node is propagated with or for interoperation with KNIME. * ------------------------------------------------------------------------ - * + * * History * Sep 5, 2012 (Patrick Winter): created */ package org.knime.base.filehandling.binaryobjectstopngs; +import static org.knime.node.impl.description.PortDescription.fixedPort; + +import java.util.List; +import java.util.Map; + +import org.knime.core.node.NodeDescription; import org.knime.core.node.NodeDialogPane; import org.knime.core.node.NodeFactory; import org.knime.core.node.NodeView; +import org.knime.core.webui.node.dialog.NodeDialog; +import org.knime.core.webui.node.dialog.NodeDialogFactory; +import org.knime.core.webui.node.dialog.NodeDialogManager; +import org.knime.core.webui.node.dialog.SettingsType; +import org.knime.core.webui.node.dialog.defaultdialog.DefaultKaiNodeInterface; +import org.knime.core.webui.node.dialog.defaultdialog.DefaultNodeDialog; +import org.knime.core.webui.node.dialog.kai.KaiNodeInterface; +import org.knime.core.webui.node.dialog.kai.KaiNodeInterfaceFactory; +import org.knime.node.impl.description.DefaultNodeDescriptionUtil; +import org.knime.node.impl.description.PortDescription; /** * NodeFactory for node. - * - * + * + * * @author Patrick Winter, KNIME AG, Zurich, Switzerland + * + * @author Tim Crundall, TNG Technology Consulting GmbH + * + * @author AI Migration Pipeline v1.2 */ -public class BinaryObjectsToPNGsNodeFactory extends NodeFactory { +@SuppressWarnings({"restriction", "removal"}) +public class BinaryObjectsToPNGsNodeFactory extends NodeFactory + implements NodeDialogFactory, KaiNodeInterfaceFactory { - /** - * {@inheritDoc} - */ @Override public BinaryObjectsToPNGsNodeModel createNodeModel() { return new BinaryObjectsToPNGsNodeModel(); } - /** - * {@inheritDoc} - */ @Override public int getNrNodeViews() { return 0; } - /** - * {@inheritDoc} - */ @Override - public NodeView createNodeView(final int viewIndex, - final BinaryObjectsToPNGsNodeModel nodeModel) { + public NodeView createNodeView( // + final int viewIndex, // + final BinaryObjectsToPNGsNodeModel nodeModel // + ) { return null; } - /** - * {@inheritDoc} - */ @Override public boolean hasDialog() { return true; } - /** - * {@inheritDoc} - */ + private static final String NODE_NAME = "Binary Objects to PNGs"; + + private static final String NODE_ICON = "./binaryobjectstopngs16x16.png"; + + private static final String SHORT_DESCRIPTION = "Converts the binary objects of a column to PNGs."; + + private static final String FULL_DESCRIPTION = "This node converts the binary object cells to PNG cells."; + + private static final List INPUT_PORTS = + List.of(fixedPort("Input table", "Table that contains the binary objects that will be converted.")); + + private static final List OUTPUT_PORTS = List + .of(fixedPort("Output table", "Input table with PNG column either appended or replacing the binary objects.")); + @Override public NodeDialogPane createNodeDialogPane() { - return new BinaryObjectsToPNGsNodeDialog(); + return NodeDialogManager.createLegacyFlowVariableNodeDialog(createNodeDialog()); + } + + @Override + public NodeDialog createNodeDialog() { + return new DefaultNodeDialog(SettingsType.MODEL, BinaryObjectsToPNGsNodeParameters.class); + } + + @Override + public NodeDescription createNodeDescription() { + return DefaultNodeDescriptionUtil.createNodeDescription( // + NODE_NAME, // + NODE_ICON, // + INPUT_PORTS, // + OUTPUT_PORTS, // + SHORT_DESCRIPTION, // + FULL_DESCRIPTION, // + List.of(), // + BinaryObjectsToPNGsNodeParameters.class, // + null, // + NodeType.Manipulator, // + List.of(), // + null // + ); + } + + @Override + public KaiNodeInterface createKaiNodeInterface() { + return new DefaultKaiNodeInterface(Map.of(SettingsType.MODEL, BinaryObjectsToPNGsNodeParameters.class)); } } diff --git a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.xml b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.xml deleted file mode 100644 index 0e8ea62b..00000000 --- a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeFactory.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - Binary Objects to PNGs - - - Converts the binary objects of a column to PNGs. - - - - - This node converts the binary object cells to PNG cells. - - - - - - - - - - - Table that contains the - binary - objects that will be converted. - - - Input table with PNG - column either appended or replacing the binary objects. - - - diff --git a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeModel.java b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeModel.java index bcd6c960..58b22ee4 100644 --- a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeModel.java +++ b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeModel.java @@ -50,6 +50,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Optional; import org.knime.base.filehandling.NodeUtils; import org.knime.core.data.DataCell; @@ -214,6 +215,15 @@ protected void reset() { */ @Override protected DataTableSpec[] configure(final DataTableSpec[] inSpecs) throws InvalidSettingsException { + // Auto-guess column selection (last BinaryObjectData-compatible column) if not set + if (m_columnselection.getStringValue() == null || m_columnselection.getStringValue().isEmpty()) { + Optional.ofNullable(inSpecs[0]).stream().flatMap(DataTableSpec::stream) // + .filter(col -> col.getType().isCompatible(BinaryObjectDataValue.class)) // + .reduce((first, second) -> second) // + .map(DataColumnSpec::getName) // + .ifPresent(m_columnselection::setStringValue); // + } + // createColumnRearranger will check the settings DataTableSpec outSpec = createColumnRearranger(inSpecs[0]).createSpec(); return new DataTableSpec[]{outSpec}; diff --git a/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParameters.java b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParameters.java new file mode 100644 index 00000000..83d6312e --- /dev/null +++ b/org.knime.base.filehandling/src/org/knime/base/filehandling/binaryobjectstopngs/BinaryObjectsToPNGsNodeParameters.java @@ -0,0 +1,179 @@ +/* + * ------------------------------------------------------------------------ + * + * Copyright by KNIME AG, Zurich, Switzerland + * Website: http://www.knime.com; Email: contact@knime.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME AG herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * ------------------------------------------------------------------------ + */ + +package org.knime.base.filehandling.binaryobjectstopngs; + +import java.util.Optional; + +import org.knime.core.data.DataColumnSpec; +import org.knime.core.data.blob.BinaryObjectDataValue; +import org.knime.core.node.InvalidSettingsException; +import org.knime.core.node.NodeSettingsRO; +import org.knime.core.node.NodeSettingsWO; +import org.knime.node.parameters.NodeParameters; +import org.knime.node.parameters.NodeParametersInput; +import org.knime.node.parameters.Widget; +import org.knime.node.parameters.migration.LoadDefaultsForAbsentFields; +import org.knime.node.parameters.migration.Migrate; +import org.knime.node.parameters.persistence.NodeParametersPersistor; +import org.knime.node.parameters.persistence.Persist; +import org.knime.node.parameters.persistence.Persistor; +import org.knime.node.parameters.updates.Effect; +import org.knime.node.parameters.updates.Effect.EffectType; +import org.knime.node.parameters.updates.EffectPredicate; +import org.knime.node.parameters.updates.EffectPredicateProvider; +import org.knime.node.parameters.updates.ParameterReference; +import org.knime.node.parameters.updates.ValueProvider; +import org.knime.node.parameters.updates.ValueReference; +import org.knime.node.parameters.updates.legacy.ColumnNameAutoGuessValueProvider; +import org.knime.node.parameters.widget.choices.ChoicesProvider; +import org.knime.node.parameters.widget.choices.Label; +import org.knime.node.parameters.widget.choices.ValueSwitchWidget; +import org.knime.node.parameters.widget.choices.util.ColumnSelectionUtil; +import org.knime.node.parameters.widget.choices.util.CompatibleColumnsProvider; + +/** + * Node parameters for Binary Objects to PNGs. + * + * @author Tim Crundall, TNG Technology Consulting GmbH + * @author AI Migration Pipeline v1.2 + */ +@LoadDefaultsForAbsentFields +@SuppressWarnings("restriction") +final class BinaryObjectsToPNGsNodeParameters implements NodeParameters { + + @Widget(title = "Column selection", + description = "Column containing the binary objects that will be converted to PNG images.") + @ChoicesProvider(BinaryObjectColumnsProvider.class) + @ValueProvider(InputColumnProvider.class) + @Persist(configKey = "columnselection") + @ValueReference(InputColumnRef.class) + String m_columnSelection = ""; + + private interface InputColumnRef extends ParameterReference { + } + + private static final class BinaryObjectColumnsProvider extends CompatibleColumnsProvider { + protected BinaryObjectColumnsProvider() { + super(BinaryObjectDataValue.class); + } + } + + private static final class InputColumnProvider extends ColumnNameAutoGuessValueProvider { + protected InputColumnProvider() { + super(InputColumnRef.class); + } + + @Override + protected Optional autoGuessColumn(final NodeParametersInput parametersInput) { + final var compatibleColumns = ColumnSelectionUtil.getCompatibleColumnsOfFirstPort( + parametersInput, BinaryObjectDataValue.class); + return compatibleColumns.isEmpty() ? Optional.empty() : + Optional.of(compatibleColumns.get(compatibleColumns.size() - 1)); + } + } + + @Widget(title = "Output handling", + description = "Determines whether to append a new PNG column or to replace the selected binary object column.") + @ValueSwitchWidget + @ValueReference(OutputModeRef.class) + @Persistor(OutputModePersistor.class) + @Migrate(loadDefaultIfAbsent = true) + OutputMode m_outputMode = OutputMode.REPLACE; + + private interface OutputModeRef extends ParameterReference { + } + + private enum OutputMode { + @Label(value = "Replace", description = "Replaces the selected binary object column with the PNG column.") + REPLACE, // + @Label(value = "Append", description = "Appends a new PNG column to the table.") + APPEND; + } + + /** + * Persists the OutputMode enum as the legacy string values "Append" / "Replace" under key "replace", + * matching via {@link ReplacePolicy#getName()}. + */ + private static final class OutputModePersistor implements NodeParametersPersistor { + + private static final String CFG_KEY = "replace"; + + @Override + public OutputMode load(final NodeSettingsRO settings) throws InvalidSettingsException { + final String value = settings.getString(CFG_KEY); + if (ReplacePolicy.APPEND.getName().equals(value)) { + return OutputMode.APPEND; + } + return OutputMode.REPLACE; + } + + @Override + public void save(final OutputMode obj, final NodeSettingsWO settings) { + final ReplacePolicy policy = obj == OutputMode.APPEND ? ReplacePolicy.APPEND : ReplacePolicy.REPLACE; + settings.addString(CFG_KEY, policy.getName()); + } + + @Override + public String[][] getConfigPaths() { + return new String[][]{{CFG_KEY}}; + } + } + + @Widget(title = "New column name", + description = "Name of the appended column. Only applicable when \"Append\" is selected.") + @Effect(predicate = IsAppend.class, type = EffectType.SHOW) + @Persist(configKey = "columnname") + String m_newColumnName = "PNG"; + + private static final class IsAppend implements EffectPredicateProvider { + @Override + public EffectPredicate init(final PredicateInitializer i) { + return i.getEnum(OutputModeRef.class).isOneOf(OutputMode.APPEND); + } + } + +}