diff --git a/ordinis-admin-ui/src/routes/webhooks.$id.tsx b/ordinis-admin-ui/src/routes/webhooks.$id.tsx index f7c2835..3f1c36a 100644 --- a/ordinis-admin-ui/src/routes/webhooks.$id.tsx +++ b/ordinis-admin-ui/src/routes/webhooks.$id.tsx @@ -33,6 +33,7 @@ import { useRetryWebhookDelivery, useRotateWebhookSecret, useTestWebhook, + useUpdateWebhook, } from '@/api/mutations' import type { WebhookTestPingResult } from '@/api/client' import { SCOPE_DOT } from '@/lib/scope-style' @@ -63,6 +64,7 @@ function WebhookDetailPage() { } const rotateMut = useRotateWebhookSecret() const deleteMut = useDeleteWebhook() + const updateMut = useUpdateWebhook() const retryMut = useRetryWebhookDelivery() const testMut = useTestWebhook() const [testResult, setTestResult] = useState(null) @@ -161,9 +163,38 @@ function WebhookDetailPage() { {data.url} - - {data.active ? t('webhooks.active') : t('webhooks.inactive')} - +
+ + {data.active ? t('webhooks.active') : t('webhooks.inactive')} + + +
{data.scopeFilter && data.scopeFilter.length > 0 ? ( diff --git a/ordinis-domain/src/main/java/cloud/nstart/terravault/ordinis/domain/webhook/WebhookSubscription.java b/ordinis-domain/src/main/java/cloud/nstart/terravault/ordinis/domain/webhook/WebhookSubscription.java index c9bef6d..6e454bb 100644 --- a/ordinis-domain/src/main/java/cloud/nstart/terravault/ordinis/domain/webhook/WebhookSubscription.java +++ b/ordinis-domain/src/main/java/cloud/nstart/terravault/ordinis/domain/webhook/WebhookSubscription.java @@ -41,10 +41,23 @@ public class WebhookSubscription { @Column(name = "url", nullable = false, length = 2048) private String url; - /** TEXT[] postgres → List via SqlTypes.ARRAY. */ + /** + * TEXT[] postgres хранит enum names (PUBLIC/INTERNAL/RESTRICTED). + * + *

Bug history: раньше field был {@code List} + + * {@code @JdbcTypeCode(SqlTypes.ARRAY)} — Hibernate сериализовал enum + * как ordinal (e.g. {@code {0}} для PUBLIC), а DB CHECK constraint + * ({@code chk_webhook_scope_filter}) требует string values из набора + * {@code 'PUBLIC','INTERNAL','RESTRICTED'}. Insert падал с + * {@code DataIntegrityViolationException} → 500 на POST с scopeFilter. + * + *

Fix: field хранит {@code List} (raw enum names). + * Getter/setter конвертируют в/из {@link DataScope} — внешний API + * (DTO + service signature + WebhookDispatcher) не меняется. + */ @JdbcTypeCode(SqlTypes.ARRAY) @Column(name = "scope_filter", columnDefinition = "TEXT[]") - private List scopeFilter; + private List scopeFilter; @JdbcTypeCode(SqlTypes.ARRAY) @Column(name = "dictionary_filter", columnDefinition = "TEXT[]") @@ -96,7 +109,10 @@ public class WebhookSubscription { public UUID getId() { return id; } public String getName() { return name; } public String getUrl() { return url; } - public List getScopeFilter() { return scopeFilter; } + public List getScopeFilter() { + if (scopeFilter == null) return null; + return scopeFilter.stream().map(DataScope::valueOf).toList(); + } public List getDictionaryFilter() { return dictionaryFilter; } public List getEventTypeFilter() { return eventTypeFilter; } public String getHmacSecret() { return hmacSecret; } @@ -108,7 +124,10 @@ public class WebhookSubscription { public void setName(String v) { this.name = v; touch(); } public void setUrl(String v) { this.url = v; touch(); } - public void setScopeFilter(List v) { this.scopeFilter = v; touch(); } + public void setScopeFilter(List v) { + this.scopeFilter = (v == null) ? null : v.stream().map(Enum::name).toList(); + touch(); + } public void setDictionaryFilter(List v) { this.dictionaryFilter = v; touch(); } public void setEventTypeFilter(List v) { this.eventTypeFilter = v; touch(); } public void setActive(boolean v) { this.active = v; touch(); }