diff --git a/backend/src/Notifo.Domain.Integrations.Abstractions/IntegrationProperty.cs b/backend/src/Notifo.Domain.Integrations.Abstractions/IntegrationProperty.cs index 3d618875..80db4098 100644 --- a/backend/src/Notifo.Domain.Integrations.Abstractions/IntegrationProperty.cs +++ b/backend/src/Notifo.Domain.Integrations.Abstractions/IntegrationProperty.cs @@ -39,6 +39,8 @@ public sealed record IntegrationProperty(string Name, PropertyType Type) public string? Pattern { get; init; } + public PropertyFormat Format { get; init; } = PropertyFormat.None; + public bool IsValid(string? input, [MaybeNullWhen(true)] out string error) { switch (Type) @@ -170,6 +172,32 @@ private bool TryGetString(string? input, [MaybeNullWhen(true)] out string error, } } + if (!string.IsNullOrWhiteSpace(input) && Format != PropertyFormat.None) + { + switch (Format) + { + case PropertyFormat.Email: + if (!Regex.IsMatch(input, ValidationPatterns.Email)) + { + error = Texts.IntegrationPropertyFormatEmail; + return false; + } + + break; + case PropertyFormat.HttpUrl: + // We only allow "http" and "https" schemas to enable the usage of URL field for HttpClient requests. + if (!Uri.TryCreate(input, UriKind.Absolute, out var uri) + || (!string.Equals(uri.Scheme, "http", StringComparison.OrdinalIgnoreCase) && !string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase)) + ) + { + error = Texts.IntegrationPropertyFormatHttpUrl; + return false; + } + + break; + } + } + return true; } diff --git a/backend/src/Notifo.Domain.Integrations.Abstractions/PropertyFormat.cs b/backend/src/Notifo.Domain.Integrations.Abstractions/PropertyFormat.cs new file mode 100644 index 00000000..d66549c6 --- /dev/null +++ b/backend/src/Notifo.Domain.Integrations.Abstractions/PropertyFormat.cs @@ -0,0 +1,15 @@ +// ========================================================================== +// Notifo.io +// ========================================================================== +// Copyright (c) Sebastian Stehle +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Notifo.Domain.Integrations; + +public enum PropertyFormat +{ + None, + Email, + HttpUrl +} diff --git a/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.Designer.cs b/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.Designer.cs index 0f9c22cd..a9c1d200 100644 --- a/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.Designer.cs +++ b/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.Designer.cs @@ -69,6 +69,24 @@ internal static string IntegrationPropertyAllowedValue { } } + /// + /// Looks up a localized string similar to Field is not a valid e-mail.. + /// + internal static string IntegrationPropertyFormatEmail { + get { + return ResourceManager.GetString("IntegrationPropertyFormatEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Field is not a valid URL (remember about the protocol - http or https).. + /// + internal static string IntegrationPropertyFormatHttpUrl { + get { + return ResourceManager.GetString("IntegrationPropertyFormatHttpUrl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Not a valid boolean value.. /// diff --git a/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.resx b/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.resx index acbb057b..3e20ce90 100644 --- a/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.resx +++ b/backend/src/Notifo.Domain.Integrations.Abstractions/Resources/Texts.resx @@ -120,6 +120,12 @@ Field is not an allowed value. + + Field is not a valid e-mail. + + + Field is not a valid URL (remember about the protocol - http or https). + Not a valid boolean value. diff --git a/backend/src/Notifo.Domain.Integrations.Abstractions/ValidationPatterns.cs b/backend/src/Notifo.Domain.Integrations.Abstractions/ValidationPatterns.cs new file mode 100644 index 00000000..11218c73 --- /dev/null +++ b/backend/src/Notifo.Domain.Integrations.Abstractions/ValidationPatterns.cs @@ -0,0 +1,14 @@ +// ========================================================================== +// Notifo.io +// ========================================================================== +// Copyright (c) Sebastian Stehle +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Notifo.Domain.Integrations; + +public static class ValidationPatterns +{ + // Taken from Yup's source code (validation library used on the front-end side). + public const string Email = @"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"; +} diff --git a/backend/src/Notifo.Domain.Integrations/AmazonSES/IntegratedAmazonSESIntegration.cs b/backend/src/Notifo.Domain.Integrations/AmazonSES/IntegratedAmazonSESIntegration.cs index 27c9b83f..49db6aed 100644 --- a/backend/src/Notifo.Domain.Integrations/AmazonSES/IntegratedAmazonSESIntegration.cs +++ b/backend/src/Notifo.Domain.Integrations/AmazonSES/IntegratedAmazonSESIntegration.cs @@ -29,11 +29,11 @@ public sealed class IntegratedAmazonSESIntegration : IIntegration, IInitializabl public static readonly IntegrationProperty FromEmailProperty = new IntegrationProperty("fromEmail", PropertyType.Text) { - Pattern = Patterns.Email, EditorLabel = Texts.Email_FromEmailLabel, EditorDescription = Texts.Email_FromEmailDescription, IsRequired = true, - Summary = true + Summary = true, + Format = PropertyFormat.Email }; public static readonly IntegrationProperty FromNameProperty = new IntegrationProperty("fromName", PropertyType.Text) diff --git a/backend/src/Notifo.Domain.Integrations/Http/HttpIntegration.cs b/backend/src/Notifo.Domain.Integrations/Http/HttpIntegration.cs index 78790411..dea8ce2b 100644 --- a/backend/src/Notifo.Domain.Integrations/Http/HttpIntegration.cs +++ b/backend/src/Notifo.Domain.Integrations/Http/HttpIntegration.cs @@ -18,7 +18,8 @@ public sealed partial class HttpIntegration : IIntegration EditorLabel = Texts.Webhook_URLLabel, EditorDescription = Texts.Webhook_URLHints, IsRequired = true, - Summary = true + Summary = true, + Format = PropertyFormat.HttpUrl, }; private static readonly IntegrationProperty HttpMethodProperty = new IntegrationProperty("Method", PropertyType.Text) diff --git a/backend/src/Notifo.Domain.Integrations/Mailchimp/MailchimpIntegration.cs b/backend/src/Notifo.Domain.Integrations/Mailchimp/MailchimpIntegration.cs index 8639ceb4..6e6e815b 100644 --- a/backend/src/Notifo.Domain.Integrations/Mailchimp/MailchimpIntegration.cs +++ b/backend/src/Notifo.Domain.Integrations/Mailchimp/MailchimpIntegration.cs @@ -22,11 +22,11 @@ public sealed partial class MailchimpIntegration : IIntegration public static readonly IntegrationProperty FromEmailProperty = new IntegrationProperty("fromEmail", PropertyType.Text) { - Pattern = Patterns.Email, EditorLabel = Texts.Email_FromEmailLabel, EditorDescription = Texts.Email_FromEmailDescription, IsRequired = true, - Summary = true + Summary = true, + Format = PropertyFormat.Email }; public static readonly IntegrationProperty FromNameProperty = new IntegrationProperty("fromName", PropertyType.Text) diff --git a/backend/src/Notifo.Domain.Integrations/Mailjet/MailjetIntegration.cs b/backend/src/Notifo.Domain.Integrations/Mailjet/MailjetIntegration.cs index 28593841..02d0002a 100644 --- a/backend/src/Notifo.Domain.Integrations/Mailjet/MailjetIntegration.cs +++ b/backend/src/Notifo.Domain.Integrations/Mailjet/MailjetIntegration.cs @@ -29,11 +29,11 @@ public sealed partial class MailjetIntegration : IIntegration public static readonly IntegrationProperty FromEmailProperty = new IntegrationProperty("fromEmail", PropertyType.Text) { - Pattern = Patterns.Email, EditorLabel = Texts.Email_FromEmailLabel, EditorDescription = Texts.Email_FromEmailDescription, IsRequired = true, - Summary = true + Summary = true, + Format = PropertyFormat.Email }; public static readonly IntegrationProperty FromNameProperty = new IntegrationProperty("fromName", PropertyType.Text) diff --git a/backend/src/Notifo.Domain.Integrations/Patterns.cs b/backend/src/Notifo.Domain.Integrations/Patterns.cs deleted file mode 100644 index b0eb9100..00000000 --- a/backend/src/Notifo.Domain.Integrations/Patterns.cs +++ /dev/null @@ -1,13 +0,0 @@ -// ========================================================================== -// Notifo.io -// ========================================================================== -// Copyright (c) Sebastian Stehle -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Notifo.Domain.Integrations; - -public static class Patterns -{ - public const string Email = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-||_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+([a-z]+|\d|-|\.{0,1}|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$"; -} diff --git a/backend/src/Notifo.Domain.Integrations/Resources/Texts.Designer.cs b/backend/src/Notifo.Domain.Integrations/Resources/Texts.Designer.cs index e18d0bf9..60c00818 100644 --- a/backend/src/Notifo.Domain.Integrations/Resources/Texts.Designer.cs +++ b/backend/src/Notifo.Domain.Integrations/Resources/Texts.Designer.cs @@ -1017,7 +1017,7 @@ internal static string Webhook_SendNotificationsLabel { } /// - /// Looks up a localized string similar to The URL to your server endpoint.. + /// Looks up a localized string similar to The URL to your server endpoint. Remember about the protocol (http, https).. /// internal static string Webhook_URLHints { get { diff --git a/backend/src/Notifo.Domain.Integrations/Resources/Texts.resx b/backend/src/Notifo.Domain.Integrations/Resources/Texts.resx index 8a738434..ed18f281 100644 --- a/backend/src/Notifo.Domain.Integrations/Resources/Texts.resx +++ b/backend/src/Notifo.Domain.Integrations/Resources/Texts.resx @@ -438,7 +438,7 @@ When you have done this send me an /update command. Send Always - The URL to your server endpoint. + The URL to your server endpoint. Remember about the protocol (http, https). URL diff --git a/backend/src/Notifo.Domain.Integrations/Smtp/SmtpIntegration.cs b/backend/src/Notifo.Domain.Integrations/Smtp/SmtpIntegration.cs index b40735d3..4c406b65 100644 --- a/backend/src/Notifo.Domain.Integrations/Smtp/SmtpIntegration.cs +++ b/backend/src/Notifo.Domain.Integrations/Smtp/SmtpIntegration.cs @@ -42,11 +42,11 @@ public sealed partial class SmtpIntegration : IIntegration public static readonly IntegrationProperty FromEmailProperty = new IntegrationProperty("fromEmail", PropertyType.Text) { - Pattern = Patterns.Email, EditorLabel = Texts.Email_FromEmailLabel, EditorDescription = Texts.Email_FromEmailDescription, IsRequired = true, - Summary = true + Summary = true, + Format = PropertyFormat.Email, }; public static readonly IntegrationProperty FromNameProperty = new IntegrationProperty("fromName", PropertyType.Text) diff --git a/backend/src/Notifo/Areas/Api/Controllers/Apps/Dtos/IntegrationPropertyDto.cs b/backend/src/Notifo/Areas/Api/Controllers/Apps/Dtos/IntegrationPropertyDto.cs index c15e846e..e3ef3f6e 100644 --- a/backend/src/Notifo/Areas/Api/Controllers/Apps/Dtos/IntegrationPropertyDto.cs +++ b/backend/src/Notifo/Areas/Api/Controllers/Apps/Dtos/IntegrationPropertyDto.cs @@ -72,6 +72,11 @@ public sealed class IntegrationPropertyDto /// public string? Pattern { get; set; } + /// + /// Format of the field, used to both validate the input and to provide hints to the user. + /// + public PropertyFormat Format { get; set; } + /// /// The default value. /// diff --git a/backend/tests/Notifo.Domain.Tests/Integrations/IntegrationPropertyTests.cs b/backend/tests/Notifo.Domain.Tests/Integrations/IntegrationPropertyTests.cs index 34b0c3a7..2e41cdc5 100644 --- a/backend/tests/Notifo.Domain.Tests/Integrations/IntegrationPropertyTests.cs +++ b/backend/tests/Notifo.Domain.Tests/Integrations/IntegrationPropertyTests.cs @@ -158,6 +158,77 @@ public void Should_not_fail_if_undefined_value_is_not_an_allowed_value(string? i Assert.Equal("allowed", property.GetString(source)); } + [Theory] + [InlineData("localhost.com/test")] + [InlineData("192.168.0.101")] + [InlineData("randomString")] + public void Should_fail_if_url_is_invalid(string? input) + { + var source = new Dictionary + { + ["key"] = input! + }; + + var property = new IntegrationProperty("key", PropertyType.Text) + { + Format = PropertyFormat.HttpUrl + }; + + Assert.Throws(() => property.GetString(source)); + } + + [Theory] + [InlineData("http://192.168.0.101/")] + [InlineData("http://localhost/test")] + [InlineData("https://example.com/test?query=example")] + [InlineData("http://login:password@test.pl/random")] + public void Should_get_url_if_value_is_valid(string? input) + { + var source = new Dictionary + { + ["key"] = input! + }; + + var property = new IntegrationProperty("key", PropertyType.Text) + { + Format = PropertyFormat.HttpUrl + }; + + Assert.Equal(input, property.GetString(source)); + } + + [Fact] + public void Should_fail_if_email_is_invalid() + { + var source = new Dictionary + { + ["key"] = "invalidEmail" + }; + + var property = new IntegrationProperty("key", PropertyType.Text) + { + Format = PropertyFormat.Email + }; + + Assert.Throws(() => property.GetString(source)); + } + + [Fact] + public void Should_get_email_if_value_is_valid() + { + var source = new Dictionary + { + ["key"] = "john.doe@example.com" + }; + + var property = new IntegrationProperty("key", PropertyType.Text) + { + Format = PropertyFormat.Email + }; + + Assert.Equal(source["key"], property.GetString(source)); + } + [Fact] public void Should_get_value_if_all_requirements_are_met() { diff --git a/frontend/src/app/pages/app/AppAuth.tsx b/frontend/src/app/pages/app/AppAuth.tsx index aad4b058..e6032748 100644 --- a/frontend/src/app/pages/app/AppAuth.tsx +++ b/frontend/src/app/pages/app/AppAuth.tsx @@ -86,7 +86,7 @@ const FormSchema = Yup.object().shape({ // Valid URL. signoutRedirectUrl: Yup.string() - .label(texts.auth.signoutRedirectUrl).urlI18n(), + .label(texts.auth.signoutRedirectUrl).httpUrlI18n(), }); const AuthForm = ({ appDetails, scheme }: { scheme?: AuthSchemeDto } & AppAuthProps) => { diff --git a/frontend/src/app/pages/app/AppSettings.tsx b/frontend/src/app/pages/app/AppSettings.tsx index fd59e7bc..a0585399 100644 --- a/frontend/src/app/pages/app/AppSettings.tsx +++ b/frontend/src/app/pages/app/AppSettings.tsx @@ -28,7 +28,7 @@ const FormSchema = Yup.object().shape({ // Valid URL confirmUrl: Yup.string().nullable() - .label(texts.app.confirmUrl).urlI18n(), + .label(texts.app.confirmUrl).httpUrlI18n(), }); export interface AppSettingsProps { diff --git a/frontend/src/app/pages/integrations/IntegrationDialog.tsx b/frontend/src/app/pages/integrations/IntegrationDialog.tsx index 66c4baf2..48786cab 100644 --- a/frontend/src/app/pages/integrations/IntegrationDialog.tsx +++ b/frontend/src/app/pages/integrations/IntegrationDialog.tsx @@ -124,6 +124,17 @@ export const IntegrationDialog = (props: IntegrationDialogProps) => { propertyType = propertyType.max(property.maxLength, texts.validation.maxLengthFn); } + if (property.format && property.format !== "None") { + switch (property.format) { + case "Email": + propertyType = propertyType.emailI18n(); + break; + case "HttpUrl": + propertyType = propertyType.httpUrlI18n(); + break; + } + } + if (property.pattern) { propertyType = propertyType.matches(new RegExp(property.pattern), texts.validation.patternFn); } @@ -271,6 +282,20 @@ export const FormField = ({ property }: { property: IntegrationPropertyDto }) => label={label} hints={property.editorDescription} /> ); } else { + if (property.format && property.format !== 'None') { + switch (property.format) { + case 'Email': + return ( + + ); + case 'HttpUrl': + return ( + + ); + } + } return ( diff --git a/frontend/src/app/pages/users/UserDialog.tsx b/frontend/src/app/pages/users/UserDialog.tsx index 1f62e956..2e11bd1a 100644 --- a/frontend/src/app/pages/users/UserDialog.tsx +++ b/frontend/src/app/pages/users/UserDialog.tsx @@ -119,7 +119,7 @@ export const UserDialog = (props: UserDialogProps) => { - { + return ( + + + + ); + } + export const Url = ({ placeholder, ...other }: FormEditorProps) => { return ( diff --git a/frontend/src/app/shared/utils/model.ts b/frontend/src/app/shared/utils/model.ts index 46f64dda..f4c4ef59 100644 --- a/frontend/src/app/shared/utils/model.ts +++ b/frontend/src/app/shared/utils/model.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved. */ -import { ChannelCondition, ChannelRequired, ChannelSend, ConfirmMode, IsoDayOfWeek, SchedulingType, TopicChannel } from '@app/service'; +import { ChannelCondition, ChannelRequired, ChannelSend, ConfirmMode, IsoDayOfWeek , SchedulingType, TopicChannel } from '@app/service'; import { texts } from '@app/texts'; export const CHANNELS = [ @@ -106,4 +106,4 @@ export const WEEK_DAYS: ReadonlyArray> = [{ }, { value: 'Saturday', label: texts.common.weekDays.saturday, -}]; \ No newline at end of file +}]; diff --git a/frontend/src/app/shared/utils/yup.ts b/frontend/src/app/shared/utils/yup.ts index 420a6082..dfbc2c29 100644 --- a/frontend/src/app/shared/utils/yup.ts +++ b/frontend/src/app/shared/utils/yup.ts @@ -13,8 +13,11 @@ function emailI18n(this: Yup.StringSchema) { return this.email(texts.validation.emailFn); } -function urlI18n(this: Yup.StringSchema) { - return this.url(texts.validation.urlFn); +function httpUrlI18n(this: Yup.StringSchema) { + // This regular expression is built on top of the one + // from Yup, but it also allows localhost. + // See: https://github.com/jquense/yup/issues/224 + return this.matches(/^(?:(?:https?):\/\/|www\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*\)|[A-Z0-9+&@#\/%=~_|$[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i , texts.validation.httpUrlFn); } function requiredI18nNumber(this: Yup.NumberSchema) { @@ -61,7 +64,7 @@ export function extendYup() { Yup.addMethod(Yup.object, 'atLeastOneStringI18n', atLeastOneStringI18n); Yup.addMethod(Yup.string, 'emailI18n', emailI18n); - Yup.addMethod(Yup.string, 'urlI18n', urlI18n); + Yup.addMethod(Yup.string, 'httpUrlI18n', httpUrlI18n); Yup.addMethod(Yup.string, 'requiredI18n', requiredI18n); Yup.addMethod(Yup.string, 'topicI18n', topicI18n); diff --git a/frontend/src/app/texts/en.ts b/frontend/src/app/texts/en.ts index 57f6d443..641ab342 100644 --- a/frontend/src/app/texts/en.ts +++ b/frontend/src/app/texts/en.ts @@ -433,6 +433,7 @@ export const EN = { validation: { atLeastOnString: (p: { label?: string }) => `${p.label} needs at least one value.`, emailFn: (p: { label?: string }) => `${p.label} must be a valid email address.`, + httpUrlFn: (p: { label?: string }) => `${p.label} must be a valid HTTP URL (include the protocol - http or https).`, lessThanFn: (p: { label?: string; less: number }) => `${p.label} must be less than ${p.less || 0}.`, maxFn: (p: { label?: string; max: number }) => `${p.label} must not greater than ${p.max}.`, maxItemsFn: (p: { label?: string; max: number }) => `${p.label} must not have more than ${p.max}.`, @@ -444,6 +445,5 @@ export const EN = { patternFn: (p: { label?: string }) => `${p.label} is not in a valid format.`, requiredFn: (p: { label?: string }) => `${p.label} is required.`, topicFn: (p: { label?: string }) => `${p.label} must be a valid topic.`, - urlFn: (p: { label?: string }) => `${p.label} must be a valid URL.`, }, }; diff --git a/frontend/src/app/texts/tr.ts b/frontend/src/app/texts/tr.ts index 70158e04..f2a3ff3e 100644 --- a/frontend/src/app/texts/tr.ts +++ b/frontend/src/app/texts/tr.ts @@ -417,6 +417,7 @@ export const TR = { validation: { atLeastOnString: (p: { label?: string }) => `${p.label} en az bir değere sahip olmalıdır.`, emailFn: (p: { label?: string }) => `${p.label} geçerli bir e-posta adresi olmalıdır.`, + httpUrlFn: (p: { label?: string }) => `${p.label} geçerli bir HTTP URL'si olmalı (protokolü ekleyin - http veya https).`, lessThanFn: (p: { label?: string; less: number }) => `${p.label} ${p.less || 0}\`dan küçük olmalıdır.`, maxFn: (p: { label?: string; max: number }) => `${p.label} ${p.max}\`dan büyük olmamalıdır.`, maxItemsFn: (p: { label?: string; max: number }) => `${p.label} en fazla ${p.max} öğeye sahip olmalıdır.`, @@ -428,6 +429,5 @@ export const TR = { patternFn: (p: { label?: string }) => `${p.label} geçerli bir formatta değil.`, requiredFn: (p: { label?: string }) => `${p.label} zorunludur.`, topicFn: (p: { label?: string }) => `${p.label} geçerli bir konu olmalıdır.`, - urlFn: (p: { label?: string }) => `${p.label} geçerli bir URL olmalıdır.`, }, }; diff --git a/frontend/src/app/typings/yup.d.ts b/frontend/src/app/typings/yup.d.ts index 5ed7c560..2e831209 100644 --- a/frontend/src/app/typings/yup.d.ts +++ b/frontend/src/app/typings/yup.d.ts @@ -6,17 +6,20 @@ */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { PropertyFormat } from '@app/service'; import { NumberSchema, ObjectSchema, StringSchema } from 'yup'; declare module 'yup' { interface StringSchema { emailI18n(): StringSchema; - urlI18n(): StringSchema; + httpUrlI18n(): StringSchema; requiredI18n(): StringSchema; topicI18n(): StringSchema; + + formatI18n(format: PropertyFormat): StringSchema; } interface NumberSchema { diff --git a/tools/sdk/Notifo.SDK/Generated.cs b/tools/sdk/Notifo.SDK/Generated.cs index 9c778de3..1c72de99 100644 --- a/tools/sdk/Notifo.SDK/Generated.cs +++ b/tools/sdk/Notifo.SDK/Generated.cs @@ -17858,6 +17858,12 @@ public partial class IntegrationPropertyDto [Newtonsoft.Json.JsonProperty("pattern", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Pattern { get; set; } + /// + /// Format of the field, used to both validate the input and to provide hints to the user. + /// + [Newtonsoft.Json.JsonProperty("format", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public PropertyFormat Format { get; set; } + /// /// The default value. /// @@ -17887,6 +17893,21 @@ public enum PropertyType } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.8.0 (NJsonSchema v11.0.1.0 (Newtonsoft.Json v13.0.0.0))")] + public enum PropertyFormat + { + + [System.Runtime.Serialization.EnumMember(Value = @"None")] + None = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Email")] + Email = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"HttpUrl")] + HttpUrl = 2, + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.3.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class IntegrationCreatedDto {