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">"</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]'
+ );
+}