This is an automated email from the ASF dual-hosted git repository.

SvenO3 pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new f4cf5d53e2 feat(#4466): Add validation and naming warnings for 
property field names in adapter schema configuration (#4643)
f4cf5d53e2 is described below

commit f4cf5d53e24344ad5ce06804df109028b1cc694c
Author: Sven Oehler <[email protected]>
AuthorDate: Mon Jun 29 10:28:55 2026 +0200

    feat(#4466): Add validation and naming warnings for property field names in 
adapter schema configuration (#4643)
    
    Co-authored-by: Dominik Riemer <[email protected]>
---
 ui/deployment/i18n/de.json                         |  6 ++
 ui/deployment/i18n/en.json                         |  6 ++
 ui/deployment/i18n/pl.json                         |  6 ++
 .../configure-schema.component.html                | 50 +++++++++++++
 .../configure-schema.component.scss                | 16 ++++
 .../configure-schema/configure-schema.component.ts | 22 +++++-
 .../configure-schema/field-name-validation.ts      | 87 ++++++++++++++++++++++
 7 files changed, 192 insertions(+), 1 deletion(-)

diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index d6ebc2dc3b..32fff84e7b 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -451,6 +451,8 @@
   "Field description": "Feldbeschreibung",
   "Field label": "Feldname",
   "Field name": "Name des Feldes",
+  "Field name recommendation": "Empfehlung für Feldnamen",
+  "Field names cannot contain": "Feldnamen dürfen Folgendes nicht enthalten",
   "Field runtime name": "Feld-Laufzeitname",
   "Field settings": "Feldeinstellungen",
   "Fields": "Felder",
@@ -561,9 +563,11 @@
   "Invalid JSON": "Ungültiges JSON",
   "Invalid URI format.": "Ungültiges URI-Format.",
   "Invalid configuration for": "Ungültige Konfiguration für",
+  "Invalid field names": "Ungültige Feldnamen",
   "Invalid pipeline: Modify to fix problems": "Ungültige Pipeline: Ändern, um 
Probleme zu beheben",
   "Invert progress semantics": "Fortschrittslogik umkehren",
   "Issuer": "Aussteller",
+  "It is recommended to use a consistent naming convention for field names 
without special characters, except": "Es wird empfohlen, eine einheitliche 
Namenskonvention für Feldnamen ohne Sonderzeichen zu verwenden, außer",
   "Items per page": "Einträge pro Seite",
   "JWT Signature": "JWT-Signature",
   "Java Runtime Name": "Name der Java-Laufzeitumgebung",
@@ -872,6 +876,7 @@
   "Rename files": "Dateien umbenennen",
   "Rename the changed fields": "Geänderte Felder umbenennen",
   "Rename the fields with changed data types by using the \"Field Renamer\" 
data processor": "Benennen Sie die Felder mit geänderten Datentypen mit dem 
Datenprozessor \"Field Renamer\" um",
+  "Rename these fields before continuing:": "Benennen Sie diese Felder um, 
bevor Sie fortfahren:",
   "Reorder asset": "Asset neu anordnen",
   "Repeat password": "Passwort wiederholen",
   "Replace existing": "Vorhandene ersetzen",
@@ -1116,6 +1121,7 @@
   "There was an error while resetting your password.": "Beim Zurücksetzen 
Ihres Passworts ist ein Fehler aufgetreten.",
   "These are default values - to use features such as email you need to store 
these values once.": "Dies sind lediglich Platzhalter - um Funktionen wie 
E-Mails zu nutzen, müssen diese Werte einmal abspeichert werden.",
   "These elements can't be connected.": "Diese Elemente können nicht verbunden 
werden.",
+  "These fields should be renamed:": "Diese Felder sollten umbenannt werden:",
   "This action cannot be reversed!": "Diese Aktion kann nicht rückgängig 
gemacht werden!",
   "This action cannot be undone!": "Diese Aktion kann nicht rückgängig gemacht 
werden!",
   "This adapter is used by": "Dieser Adapter wird verwendet von",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index bb65b520b2..865f7a11d5 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -451,6 +451,8 @@
   "Field description": null,
   "Field label": null,
   "Field name": null,
+  "Field name recommendation": null,
+  "Field names cannot contain": null,
   "Field runtime name": null,
   "Field settings": null,
   "Fields": null,
@@ -561,9 +563,11 @@
   "Invalid JSON": null,
   "Invalid URI format.": null,
   "Invalid configuration for": null,
+  "Invalid field names": null,
   "Invalid pipeline: Modify to fix problems": null,
   "Invert progress semantics": null,
   "Issuer": null,
+  "It is recommended to use a consistent naming convention for field names 
without special characters, except": null,
   "Items per page": null,
   "JWT Signature": null,
   "Java Runtime Name": null,
@@ -872,6 +876,7 @@
   "Rename files": null,
   "Rename the changed fields": null,
   "Rename the fields with changed data types by using the \"Field Renamer\" 
data processor": null,
+  "Rename these fields before continuing:": null,
   "Reorder asset": null,
   "Repeat password": null,
   "Replace existing": null,
@@ -1116,6 +1121,7 @@
   "There was an error while resetting your password.": null,
   "These are default values - to use features such as email you need to store 
these values once.": null,
   "These elements can't be connected.": null,
+  "These fields should be renamed:": null,
   "This action cannot be reversed!": null,
   "This action cannot be undone!": null,
   "This adapter is used by": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index cf392018df..5557760100 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -451,6 +451,8 @@
   "Field description": "Opis pola",
   "Field label": "Etykieta pola",
   "Field name": "Nazwa pola",
+  "Field name recommendation": "Zalecenie dotyczące nazw pól",
+  "Field names cannot contain": "Nazwy pól nie mogą zawierać",
   "Field runtime name": "Nazwa pola w środowisku wykonawczym",
   "Field settings": "Ustawienia pola",
   "Fields": "Pola",
@@ -561,9 +563,11 @@
   "Invalid JSON": "Nieprawidłowy JSON",
   "Invalid URI format.": "Nieprawidłowy format URI.",
   "Invalid configuration for": "Nieprawidłowa konfiguracja dla",
+  "Invalid field names": "Nieprawidłowe nazwy pól",
   "Invalid pipeline: Modify to fix problems": "Nieprawidłowy strumień: 
zmodyfikuj, aby naprawić problemy",
   "Invert progress semantics": "Odwróć logikę postępu",
   "Issuer": "Wystawca",
+  "It is recommended to use a consistent naming convention for field names 
without special characters, except": "Zaleca się stosowanie spójnej konwencji 
nazewnictwa pól bez znaków specjalnych, z wyjątkiem",
   "Items per page": "Elementów na stronę",
   "JWT Signature": "Podpis JWT",
   "Java Runtime Name": "Nazwa srodowiska Java Runtime",
@@ -872,6 +876,7 @@
   "Rename files": "Zmień nazwy plików",
   "Rename the changed fields": "Zmień nazwy zmienionych pól",
   "Rename the fields with changed data types by using the \"Field Renamer\" 
data processor": "Zmień nazwy pól ze zmienionymi typami danych za pomocą 
procesora danych \"Field Renamer\"",
+  "Rename these fields before continuing:": "Zmień nazwy tych pól przed 
kontynuowaniem:",
   "Reorder asset": "Zmień kolejność zasobu",
   "Repeat password": "Powtórz hasło",
   "Replace existing": "Zastąp istniejące",
@@ -1116,6 +1121,7 @@
   "There was an error while resetting your password.": "Wystąpił błąd podczas 
resetowania hasła.",
   "These are default values - to use features such as email you need to store 
these values once.": "To są wartości domyślne — aby używać funkcji, takich jak 
e-mail, musisz zapisać te wartości.",
   "These elements can't be connected.": "Tych elementów nie można połączyć.",
+  "These fields should be renamed:": "Należy zmienić nazwy tych pól:",
   "This action cannot be reversed!": "Tego działania nie można odwrócić!",
   "This action cannot be undone!": "Tego działania nie można cofnąć!",
   "This adapter is used by": "Ten adapter jest używany przez",
diff --git 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
index 3b3be24a73..6f36ce6e17 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
+++ 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
@@ -16,6 +16,56 @@
 ~
 -->
 <div fxFlex="100" fxLayout="column">
+    @if (invalidFieldNames().length > 0) {
+        <sp-alert-banner
+            type="error"
+            [title]="'Invalid field names' | translate"
+            data-cy="configure-schema-invalid-field-names"
+        >
+            <div class="field-name-message text-sm">
+                {{ 'Field names cannot contain' | translate }}
+                <span class="field-name-character">,</span>,
+                <span class="field-name-character">&quot;</span>,
+                <span class="field-name-character">;</span>.
+                {{ 'Rename these fields before continuing:' | translate }}
+            </div>
+            @for (fieldName of invalidFieldNames(); track fieldName) {
+                <div
+                    class="font-bold text-sm"
+                    data-cy="configure-schema-invalid-field-name"
+                >
+                    {{ fieldName }}
+                </div>
+            }
+        </sp-alert-banner>
+    }
+
+    @if (warningFieldNames().length > 0) {
+        <sp-alert-banner
+            type="warning"
+            [title]="'Field name recommendation' | translate"
+            data-cy="configure-schema-warning-field-names"
+        >
+            <div class="field-name-message text-sm">
+                {{
+                    'It is recommended to use a consistent naming convention 
for field names without special characters, except'
+                        | translate
+                }}
+                <span class="field-name-character">-</span>,
+                <span class="field-name-character">_</span>.
+                {{ 'These fields should be renamed:' | translate }}
+            </div>
+            @for (fieldName of warningFieldNames(); track fieldName) {
+                <div
+                    class="font-bold text-sm"
+                    data-cy="configure-schema-warning-field-name"
+                >
+                    {{ fieldName }}
+                </div>
+            }
+        </sp-alert-banner>
+    }
+
     <div fxFlex="100" fxLayout="column">
         <sp-adapter-script-editor
             [scriptActive]="scriptActive()"
diff --git 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
index cf0f1a803c..d7836c00d1 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
+++ 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
@@ -23,3 +23,19 @@
 .stepper-button {
     margin-left: 10px;
 }
+
+.field-name-message {
+    margin-top: 0.25rem;
+    line-height: 1.4;
+}
+
+.field-name-character {
+    display: inline-block;
+    min-width: 1.25rem;
+    padding: 0 0.25rem;
+    border-radius: 0.25rem;
+    background: rgba(0, 0, 0, 0.08);
+    font-family: monospace;
+    font-weight: 700;
+    text-align: center;
+}
diff --git 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
index afc0eb52c3..c663040b38 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
+++ 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
@@ -38,6 +38,7 @@ import {
     ConfirmDialogComponent,
     DialogService,
     PanelType,
+    SpAlertBannerComponent,
 } from '@streampipes/shared-ui';
 import { CreateAdapterTransformationTemplateDialogComponent } from 
'../../../dialog/create-adapter-transformation-template-dialog/create-adapter-transformation-template-dialog.component';
 import { TranslatePipe, TranslateService } from '@ngx-translate/core';
@@ -57,6 +58,7 @@ import { AdapterSamplePreviewComponent } from 
'./sample-preview/adapter-sample-p
 import { AdapterResultPreviewComponent } from 
'./result-preview/adapter-result-preview.component';
 import { MatButton } from '@angular/material/button';
 import type { editor as MonacoEditor } from 'monaco-editor';
+import { validateFieldNames } from './field-name-validation';
 
 @Component({
     selector: 'sp-configure-schema',
@@ -72,6 +74,7 @@ import type { editor as MonacoEditor } from 'monaco-editor';
         LayoutAlignDirective,
         MatButton,
         TranslatePipe,
+        SpAlertBannerComponent,
     ],
 })
 export class ConfigureSchemaComponent implements OnInit {
@@ -147,6 +150,22 @@ export class ConfigureSchemaComponent implements OnInit {
                 ?.outputs?.[0] || {},
     );
 
+    schemaEvent = computed(() =>
+        this.scriptActive() ? this.output() : this.input(),
+    );
+
+    fieldNameValidation = computed(() =>
+        validateFieldNames(this.schemaEvent()),
+    );
+
+    invalidFieldNames = computed(
+        () => this.fieldNameValidation().invalidFieldNames,
+    );
+
+    warningFieldNames = computed(
+        () => this.fieldNameValidation().warningFieldNames,
+    );
+
     isNextDisabled = computed(() => {
         const state = this.stateService.state();
         const hasInputEvents =
@@ -158,7 +177,8 @@ export class ConfigureSchemaComponent implements OnInit {
             state.isRunningScript ||
             !!state.sampleError ||
             !!state.scriptError ||
-            !hasInputEvents
+            !hasInputEvents ||
+            this.invalidFieldNames().length > 0
         );
     });
 
diff --git 
a/ui/src/app/connect/components/adapter-configuration/configure-schema/field-name-validation.ts
 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/field-name-validation.ts
new file mode 100644
index 0000000000..af4d86b73f
--- /dev/null
+++ 
b/ui/src/app/connect/components/adapter-configuration/configure-schema/field-name-validation.ts
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+export interface FieldNameValidationResult {
+    invalidFieldNames: string[];
+    warningFieldNames: string[];
+}
+
+const INVALID_FIELD_NAME_CHARACTERS_REGEX = /[,";]/;
+const RECOMMENDED_FIELD_NAME_REGEX = /^[A-Za-z0-9_-]+$/;
+
+export function validateFieldNames(event: unknown): FieldNameValidationResult {
+    const invalidFieldNames = new Set<string>();
+    const warningFieldNames = new Set<string>();
+
+    validateFieldNamesRecursively(
+        event,
+        '',
+        invalidFieldNames,
+        warningFieldNames,
+    );
+
+    return {
+        invalidFieldNames: Array.from(invalidFieldNames),
+        warningFieldNames: Array.from(warningFieldNames),
+    };
+}
+
+function validateFieldNamesRecursively(
+    value: unknown,
+    currentPath: string,
+    invalidFieldNames: Set<string>,
+    warningFieldNames: Set<string>,
+): void {
+    if (Array.isArray(value)) {
+        value.forEach(item =>
+            validateFieldNamesRecursively(
+                item,
+                currentPath ? `${currentPath}[]` : '[]',
+                invalidFieldNames,
+                warningFieldNames,
+            ),
+        );
+    } else if (isRecord(value)) {
+        Object.entries(value).forEach(([fieldName, fieldValue]) => {
+            const fieldPath = currentPath
+                ? `${currentPath}.${fieldName}`
+                : fieldName;
+
+            if (INVALID_FIELD_NAME_CHARACTERS_REGEX.test(fieldName)) {
+                invalidFieldNames.add(fieldPath);
+            } else if (!RECOMMENDED_FIELD_NAME_REGEX.test(fieldName)) {
+                warningFieldNames.add(fieldPath);
+            }
+
+            validateFieldNamesRecursively(
+                fieldValue,
+                fieldPath,
+                invalidFieldNames,
+                warningFieldNames,
+            );
+        });
+    }
+}
+
+function isRecord(value: unknown): value is Record<string, unknown> {
+    return (
+        value !== null &&
+        typeof value === 'object' &&
+        Object.prototype.toString.call(value) === '[object Object]'
+    );
+}

Reply via email to