Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce PropertyFormat to avoid validation errors that can lead to internal exceptions #248

Merged
merged 12 commits into from
Aug 26, 2024

Conversation

Arciiix
Copy link
Contributor

@Arciiix Arciiix commented Jul 4, 2024

Fixes #247 , more info there.

Can you please review my code for now so that I can make some appropriate changes?

Cheers

Copy link
Collaborator

@SebastianStehle SebastianStehle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main thing is that you have forgotten the server side validation and I am not so happy with client side validation yet. See comments for more infos. Continue with the good work :)

@@ -42,7 +42,6 @@ public sealed partial class SmtpIntegration : IIntegration

public static readonly IntegrationProperty FromEmailProperty = new IntegrationProperty("fromEmail", PropertyType.Text)
{
Pattern = Patterns.Email,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put this property at the end, just like in the other property?. My brain is annoying sometimes ;)

if (property.format && property.format !== "None") {
const format = FORMAT_REGEXPS.get(property.format);


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove double line.



if (format) {
propertyType = propertyType.matches(format, texts.validation.formatFn);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup already provides validation method for email and url. Just use that. There is also emailI18n and perhaps url18n for localized methods in the code base.

Copy link
Contributor Author

@Arciiix Arciiix Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heyy @SebastianStehle! I'm back.

I just realized that the Yup's provided URL validator doesn't accept localhost (and there is no option to change that - e.g. in validate.js you can set the config to { allowLocal: true }, but there is no such thing here since it's a wrapper around .match(urlRegex))

Here's how it looks like in Yup's source code

https://github.com/jquense/yup/blob/5a22c16dbba610050e85f123d389ddacaa92a0ad/src/string.ts#L24-L26

There is also an open pull request and issue on Yup's repo that suggests adding it.

Therefore - can I modify the codebase's urli18n method and use my own RegEx, inspired by the original one but accepting localhost? And in the future, if Yup implements it, we can migrate back to the provided method.

On one hand, maybe people wouldn't use localhost in a production environment of a deployed app, but there is always a chance if they do self-deploy (instead of using http://notifo.io) or just test, like I do.

What do you think?

Copy link
Collaborator

@SebastianStehle SebastianStehle Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes perfectly sense to support localhost :)

Go for the regex, good job. But if you can use a custom function or so I would prefer that to make the regex an internal implementation detail.

@@ -441,6 +441,7 @@ export const EN = {
minItemsFn: (p: { label?: string; min: number }) => `${p.label} must have at least ${p.min || 0} items.`,
minLengthFn: (p: { label?: string; min: number }) => `${p.label} must have at least ${p.min} characters.`,
moreThanFn: (p: { label?: string; more: number }) => `${p.label} must be greater than ${p.more || 0}.`,
formatFn: (p: { label?: string }) => `${p.label} is not in a valid format for this type of field.`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have introduced the format property to provide better validation messages. Otherwise we could just use "pattern", which has a very generic method.

@@ -39,6 +39,8 @@ public sealed record IntegrationProperty(string Name, PropertyType Type)

public string? Pattern { get; init; }

public PropertyFormat Format { get; init; } = PropertyFormat.None;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation is missing in the TryGetString method.

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 17, 2024

Hey, just pushed the changes, can you please take a look if it's alright?

Copy link
Collaborator

@SebastianStehle SebastianStehle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only 2 small things to improve.

Good job :)

}

[Theory]
[InlineData("http://localhost/test")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A test with http://<IP> might be useful. Perhaps you also have it above

@@ -57,13 +61,25 @@ function atLeastOneStringI18n(this: Yup.ObjectSchema<any>) {
});
}

function formatI18n(this: Yup.StringSchema, format: PropertyFormat) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not make this. It creates a very weird dependency from a utility class to the domain layer (service). You only need this method in one place anyway, so it does not reduces the amount of code.

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 17, 2024

Hey! Thank you so much! I'm happy that there were some things I could've improved :)

Just pushed the changes

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 17, 2024

Btw,
Do you think we should add a default case handling here with a simple console.warn() that the property format is not recognized?
You didn't do that in other places, so I didn't do it as well - I'd just like to hear your opinion.

https://github.com/Arciiix/notifo/blob/b1d331f386f0f62999666c4b9983d15384517dfd/frontend/src/app/pages/integrations/IntegrationDialog.tsx#L127-L136

@SebastianStehle
Copy link
Collaborator

No, I dont think so.

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 17, 2024

Okay.
So do you think everything's ready to merge? Or do you want me to do something more (I will unify the phone numbers in another PR)?

@SebastianStehle
Copy link
Collaborator

The build fails ;) ... so it is not ready yet.

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 18, 2024

I think I forgot to update the C#'s SDK (I only updated the TypeScript one)... stupid me ;)
Thank you for noting!

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 18, 2024

Actually never mind - I updated it as well.
I'll investigate a bit because this error seems to be weird - everything was working in my local env.

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 18, 2024

It's especially weird that the same tests that fail in the GitHub Action actually pass on my local machine

passing

Do you think I should create a dummy (e.g. adding a comment) commit to re-run it again?

@SebastianStehle
Copy link
Collaborator

It also failed with previous commits. Just try it

@@ -185,6 +185,7 @@ private bool TryGetString(string? input, [MaybeNullWhen(true)] out string error,

break;
case PropertyFormat.Url:
// We only allow http and https to enable the usage of URL field for HttpClient requests.
if (!Uri.TryCreate(input, UriKind.Absolute, out var uri) || !((string[])["http", "https"]).Contains(uri.Scheme, StringComparer.OrdinalIgnoreCase))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please introduce a static or improve the query. But having an allocation for this is really weird ;)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test for that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is about mqtt:// and all other URL properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! Sure, what other URL schemes do you think should be supported, besides http, https, mqtt?
I didn't add mqtt and other schemes (e.g. smtp) at first since this URL field was specifically supposed to be used for HTTP requests, but sure - we can make it be more universal. Or maybe should we create two PropertyFormats - one for HTTP URLs, one for other types?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL should support all schemes. If an integration needs a special URL, it could just use patterns I think. You usually also get a runtime error so we should not validate too much.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me the advantage of the propertyFormat is that we can also use it in the UI and use specialized inputs for example. If we are just using that for validation we would be probably fine with regex. I don't want to make it too complicated.

But if we introduce base classes and stuff like that it makes the UI super complicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sure, you're right. Therefore - I'll rename the "Url" PropertyFormat to "HttpUrl" and only add http and https as allowed schemas.
Should I also add a generic "Url" PropertyFormat? (for input[type="url"] on front-end, so in the end, there will be a separate Url and HttpUrl)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a URL is not http or https they very like need additional validation, e.g. if it starts with "mongo://" or something that. So HttpUrl is fine.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is still the extra allocation per call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah, sure, I'm so sorry about forgetting to change that😅

@Arciiix
Copy link
Contributor Author

Arciiix commented Jul 18, 2024

Oh, so it apparently worked 🤣

I think I can quote the popular "My code doesn't work, I have no idea why" and "My code works, I have no idea why".

Also, it actually worked on the 2nd previous commit (8a9dbcf), but didn't work on the previous one which actually didn't change anything on the back-end side (b1d331f). That's weird.

Have a nice evening!

@Arciiix
Copy link
Contributor Author

Arciiix commented Aug 26, 2024

Just renamed the "Url" PropertyFormat to "HttpUrl". Can you take a look if everything is looking good?

@SebastianStehle
Copy link
Collaborator

SebastianStehle commented Aug 26, 2024

Only the one thing with allocations.

if (!Uri.TryCreate(input, UriKind.Absolute, out var uri) || !((string[])["http", "https"]).Contains(uri.Scheme, StringComparer.OrdinalIgnoreCase))
// 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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just create a static readonly field with HttpSchemes, would have been shorter ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I do it in another pull request? I see you already merged this one :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, plz :)

@SebastianStehle SebastianStehle merged commit c9d7cf7 into notifo-io:main Aug 26, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing URL validation that can lead to internal error
2 participants