diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/builtinparams/MaxBatchSizeParamBuilder.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/builtinparams/MaxBatchSizeParamBuilder.java index a766af0115732..0022052816151 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/builtinparams/MaxBatchSizeParamBuilder.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/builtinparams/MaxBatchSizeParamBuilder.java @@ -40,7 +40,7 @@ public MaxBatchSizeParamBuilder(final ParameterMeta root, final String component final LocalConfiguration configuration) { this.root = root; this.layoutType = getLayoutType(root); - this.defaultValue = !isActive(componentSimpleClassName, configuration) ? -1 + this.defaultValue = !isActive(componentSimpleClassName, configuration) ? -4 : Integer .parseInt(ofNullable(configuration.get(componentSimpleClassName + "._maxBatchSize.value")) .orElseGet(() -> ofNullable(configuration.get("_maxBatchSize.value")).orElse("1000")) @@ -55,7 +55,7 @@ private boolean isActive(final String componentSimpleClassName, final LocalConfi } public ParameterMeta newBulkParameter() { - return defaultValue <= 0 ? null : new ParameterMeta(new ParameterMeta.Source() { + return defaultValue <= -3 ? null : new ParameterMeta(new ParameterMeta.Source() { @Override public String name() { @@ -73,7 +73,8 @@ public Class declaringClass() { { put("tcomp::ui::defaultvalue::value", String.valueOf(defaultValue)); - put("tcomp::validation::min", "1"); + put("tcomp::validation::min", "-3"); + put("tcomp::ui::hidden", "true"); } }, true); } diff --git a/component-studio/component-runtime-di/src/main/java/org/talend/sdk/component/runtime/di/AutoChunkProcessor.java b/component-studio/component-runtime-di/src/main/java/org/talend/sdk/component/runtime/di/AutoChunkProcessor.java index 1c42db63da34a..e469d06e6761f 100644 --- a/component-studio/component-runtime-di/src/main/java/org/talend/sdk/component/runtime/di/AutoChunkProcessor.java +++ b/component-studio/component-runtime-di/src/main/java/org/talend/sdk/component/runtime/di/AutoChunkProcessor.java @@ -26,4 +26,5 @@ public class AutoChunkProcessor extends org.talend.sdk.component.runtime.manager public AutoChunkProcessor(final int chunkSize, final Processor processor) { super(chunkSize, processor); } + } diff --git a/sample-parent/sample-connector/pom.xml b/sample-parent/sample-connector/pom.xml index 80f982d04a13c..4602397b74de3 100644 --- a/sample-parent/sample-connector/pom.xml +++ b/sample-parent/sample-connector/pom.xml @@ -13,8 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - + 4.0.0 @@ -61,6 +60,18 @@ 1.65.0-SNAPSHOT compile + + org.talend.sdk.component + component-runtime-manager + 1.65.0-SNAPSHOT + test + + + org.talend.sdk.component + component-runtime-junit + 1.65.0-SNAPSHOT + test + @@ -113,7 +124,6 @@ true true true - true true true true diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/output/WithAfterGroupOnlyOnce.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/output/WithAfterGroupOnlyOnce.java new file mode 100644 index 0000000000000..1991f42e21507 --- /dev/null +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/output/WithAfterGroupOnlyOnce.java @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2006-2024 Talend Inc. - www.talend.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.talend.sdk.component.test.connectors.output; + +import java.io.Serializable; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.talend.sdk.component.api.component.Icon; +import org.talend.sdk.component.api.component.Version; +import org.talend.sdk.component.api.configuration.Option; +import org.talend.sdk.component.api.configuration.ui.layout.GridLayout; +import org.talend.sdk.component.api.meta.Documentation; +import org.talend.sdk.component.api.processor.AfterGroup; +import org.talend.sdk.component.api.processor.ElementListener; +import org.talend.sdk.component.api.processor.Input; +import org.talend.sdk.component.api.processor.Output; +import org.talend.sdk.component.api.processor.OutputEmitter; +import org.talend.sdk.component.api.processor.Processor; +import org.talend.sdk.component.api.record.Record; +import org.talend.sdk.component.api.service.record.RecordBuilderFactory; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * This output connector should consume all input record but have + * only one call to @AfterGroup method after all input records are consumed. + */ + +@Slf4j +@Version(1) +@Icon(value = Icon.IconType.CUSTOM, custom = "output") +@Processor(name = "WithAfterGroupOnlyOnce") +@Documentation("Consume all input records and should have only 1 call to @AfterGroup.") +public class WithAfterGroupOnlyOnce implements Serializable { + + private final RecordBuilderFactory recordBuilderFactory; + + private final WithAfterGroupOnlyOnceConfig config; + + private int nbConsumedRecords; + + private boolean afterGroupCalled; + + public WithAfterGroupOnlyOnce(final @Option("configuration") WithAfterGroupOnlyOnceConfig config, + final RecordBuilderFactory recordBuilderFactory) { + this.recordBuilderFactory = recordBuilderFactory; + this.config = config; + } + + @PostConstruct + public void init() { + this.nbConsumedRecords = 0; + } + + @PreDestroy + public void release() { + if (!this.afterGroupCalled) { + throw new RuntimeException("The @AfterGroup method has not been called."); + } + } + + @ElementListener + public void onNext(@Input final Record record) { + this.nbConsumedRecords++; + } + + @AfterGroup + public void afterGroup(@Output("REJECT") final OutputEmitter rejected) { + if (this.afterGroupCalled) { + Record error = this.recordBuilderFactory.newRecordBuilder() + .withString("error", + "The @AfterGroup method has been called more than once.") + .build(); + rejected.emit(error); + } + + if (this.nbConsumedRecords != this.config.getExpectedNumberOfRecords()) { + Record error = this.recordBuilderFactory.newRecordBuilder() + .withString("error", + String.format("The number of consumed records '%s' is not the expected one %s.", + this.nbConsumedRecords, this.config.getExpectedNumberOfRecords())) + .build(); + rejected.emit(error); + } + + this.afterGroupCalled = true; + } + + @Data + @GridLayout({ @GridLayout.Row({ "expectedNumberOfRecords" }) }) + public static class WithAfterGroupOnlyOnceConfig implements Serializable { + + @Option + @Documentation("The number of expected record processed when @AfterGroup is called.") + private int expectedNumberOfRecords; + + } + +} diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/package-info.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/package-info.java index 1466ed5af06ea..a3df9dce1da7d 100644 --- a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/package-info.java +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/package-info.java @@ -14,7 +14,7 @@ * limitations under the License. */ @Components( - family = "the_family", + family = "the_family", categories = "the_category_1") @Icon( value = Icon.IconType.CUSTOM, diff --git a/sample-parent/sample-connector/src/main/resources/TALEND-INF/local-configuration.properties b/sample-parent/sample-connector/src/main/resources/TALEND-INF/local-configuration.properties new file mode 100644 index 0000000000000..eaed36725426f --- /dev/null +++ b/sample-parent/sample-connector/src/main/resources/TALEND-INF/local-configuration.properties @@ -0,0 +1,2 @@ +WithAfterGroupOnlyOnce._maxBatchSize.value=-2 +WithAfterGroupOnlyOnce._maxBatchSize.active=true \ No newline at end of file diff --git a/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/output/Messages.properties b/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/output/Messages.properties index 0178c52fea789..9e6bf0299cd79 100644 --- a/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/output/Messages.properties +++ b/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/output/Messages.properties @@ -18,4 +18,8 @@ # $ python i18n_messages_properties_generator.py the_family.TheOutput1._displayName = Name: The Output 1 -the_family.TheOutput1._documentation = Doc: This is a sample output. \ No newline at end of file +the_family.TheOutput1._documentation = Doc: This is a sample output. +WithAfterGroupOnlyOnceConfig.expectedNumberOfRecords._displayName = +WithAfterGroupOnlyOnceConfig.expectedNumberOfRecords._placeholder = + +the_family.WithAfterGroupOnlyOnce._displayName = WithAfterGroupOnlyOnce \ No newline at end of file diff --git a/sample-parent/sample-connector/src/test/java/org/talend/sdk/component/test/connectors/output/OutputTest.java b/sample-parent/sample-connector/src/test/java/org/talend/sdk/component/test/connectors/output/OutputTest.java new file mode 100644 index 0000000000000..0f66062a27cb1 --- /dev/null +++ b/sample-parent/sample-connector/src/test/java/org/talend/sdk/component/test/connectors/output/OutputTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2006-2024 Talend Inc. - www.talend.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.talend.sdk.component.test.connectors.output; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.talend.sdk.component.api.record.Record; +import org.talend.sdk.component.api.service.record.RecordBuilderFactory; +import org.talend.sdk.component.junit.BaseComponentsHandler; +import org.talend.sdk.component.junit.SimpleFactory; +import org.talend.sdk.component.junit5.Injected; +import org.talend.sdk.component.junit5.WithComponents; +import org.talend.sdk.component.runtime.manager.chain.Job; + +@WithComponents("org.talend.sdk.component.test.connectors") +public class OutputTest { + + @Injected + protected BaseComponentsHandler componentsHandler; + + private final String testStringValue = "test"; + + private final boolean testBooleanValue = true; + + //@Test + void testOutput() { + final int recordSize = 15; + WithAfterGroupOnlyOnce.WithAfterGroupOnlyOnceConfig config = new WithAfterGroupOnlyOnce.WithAfterGroupOnlyOnceConfig(); + config.setExpectedNumberOfRecords(recordSize); + String sourceConfig = + SimpleFactory.configurationByExample().forInstance(config).configured().toQueryString(); + + Record testRecord = componentsHandler + .findService(RecordBuilderFactory.class) + .newRecordBuilder() + .withString("stringValue", testStringValue) + .withBoolean("booleanValue", testBooleanValue) + .build(); + + List testRecords = new ArrayList<>(); + for (int i = 0; i < recordSize; i++) { + testRecords.add(testRecord); + } + componentsHandler.setInputData(testRecords); + + Job + .components() + .component("inputFlow", "test://emitter") + .component("outputComponent", "SampleConnector://WithAfterGroupOnlyOnce?$maxBatchSize=-1&" + sourceConfig) + .connections() + .from("inputFlow") + .to("outputComponent") + .build() + .run(); + + Assert.assertTrue(true); + } +} diff --git a/talend-component-maven-plugin/src/it/web/test/tck-component-index-api-test/tck_component_index_api_test.json b/talend-component-maven-plugin/src/it/web/test/tck-component-index-api-test/tck_component_index_api_test.json index f6f179e17e275..55eb0fe769d8f 100644 --- a/talend-component-maven-plugin/src/it/web/test/tck-component-index-api-test/tck_component_index_api_test.json +++ b/talend-component-maven-plugin/src/it/web/test/tck-component-index-api-test/tck_component_index_api_test.json @@ -163,7 +163,7 @@ "value": "200" }, { - "comparison": "LengthEqual", + "comparison": "LengthGreaterThanOrEqual", "subject": "ResponseJsonBody", "path": "$.components", "value": "9"