diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index e99811974480..f5f905fd3f1f 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -817,7 +817,7 @@ type Date Format "2020-06-21" with French locale as "21. juin 2020" example_format = Date.new 2020 6 21 . format (Date_Time_Formatter.from "d. MMMM yyyy" (Locale.new "fr")) - @format (value-> make_date_format_selector value) + @format make_date_format_selector format : Date_Time_Formatter -> Text format self format:Date_Time_Formatter=Date_Time_Formatter.iso_date = format.format_date self diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index 7dc1506e1806..4efdebd57b77 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -925,7 +925,7 @@ type Date_Time example_format = Date_Time.parse "2020-06-21T16:41:13+03:00" . format "d. MMMM yyyy" (Locale.new "fr") - @format (value-> make_date_time_format_selector value) + @format make_date_time_format_selector format : Date_Time_Formatter -> Text format self format:Date_Time_Formatter = format.format_date_time self diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index d75a5b92ecfe..e362335c9127 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -486,7 +486,7 @@ type Time_Of_Day from Standard.Base import Time_Of_Day example_format = Time_Of_Day.new 16 21 10 . format "'hour:'h" - @format (value-> make_time_format_selector value) + @format make_time_format_selector format : Date_Time_Formatter -> Text format self format:Date_Time_Formatter = format.format_time self diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/URI.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/URI.enso index 89a74eea1598..9e234b8295e0 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/URI.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/URI.enso @@ -4,6 +4,7 @@ import project.Data.Ordering.Comparable import project.Data.Ordering.Ordering import project.Data.Pair.Pair import project.Data.Text.Text +import project.Data.Text.Extensions import project.Data.Vector.No_Wrap import project.Data.Vector.Vector import project.Enso_Cloud.Enso_Secret.Enso_Secret @@ -16,12 +17,14 @@ import project.Errors.Problem_Behavior.Problem_Behavior import project.Meta import project.Nothing.Nothing import project.Panic.Panic +import project.Warning.Warning from project.Data.Boolean.Boolean import False, True from project.Data.Text.Extensions import all from project.Enso_Cloud.Enso_Secret import as_hideable_value from project.Widget_Helpers import make_text_secret_selector polyglot java import java.net.URI as Java_URI +polyglot java import java.net.URLEncoder polyglot java import java.net.URISyntaxException polyglot java import org.enso.base.enso_cloud.EnsoSecretAccessDenied polyglot java import org.enso.base.net.URITransformer @@ -35,12 +38,13 @@ type URI ICON convert Parse a URI from text. + If the input text is not a valid URI, this function will separate the + query string and encode it. If it still cannot be parsed, it will throw + a `Syntax_Error`. Arguments: - uri: The text to parse as a URI. - Throws a Syntax_Error when the text cannot be parsed as a URI. - > Example Parse URI text. @@ -50,10 +54,17 @@ type URI parse : Text -> URI ! Syntax_Error parse uri:Text = Panic.catch URISyntaxException (URI.Value (Java_URI.new uri) []) caught_panic-> - message = caught_panic.payload.getMessage - truncated = if message.is_nothing || message.length > 100 then "Invalid URI '" + uri.to_display_text + "'" else - "URI syntax error: " + message - Error.throw (Syntax_Error.Error truncated) + query_index = uri.index_of '?' + result = if query_index.is_nothing then Nothing else + new_uri = (uri.take query_index+1) + (URITransformer.encodeQuery (uri.drop query_index+1)) + warning = Invalid_Query_String.Warning (uri.drop query_index+1) + Panic.catch URISyntaxException (Warning.attach warning URI.Value (Java_URI.new new_uri) []) _->Nothing + + if result.is_nothing.not then result else + message = caught_panic.payload.getMessage + truncated = if message.is_nothing || message.length > 100 then "Invalid URI '" + uri.to_display_text + "'" else + "URI syntax error: " + message + Error.throw (Syntax_Error.Error truncated) ## GROUP Metadata ICON metadata @@ -326,3 +337,12 @@ type URI_Comparator ## PRIVATE Comparable.from (_:URI) = URI_Comparator + +## PRIVATE +type Invalid_Query_String + ## PRIVATE + Warning (query_string:Text) + + ## PRIVATE + to_display_text self = + "The query string contained invalid characters and has been URL-encoded ("+self.query_string+")." diff --git a/std-bits/base/src/main/java/org/enso/base/net/URITransformer.java b/std-bits/base/src/main/java/org/enso/base/net/URITransformer.java index 31aedcf1d852..14c366935c1e 100644 --- a/std-bits/base/src/main/java/org/enso/base/net/URITransformer.java +++ b/std-bits/base/src/main/java/org/enso/base/net/URITransformer.java @@ -7,7 +7,6 @@ /** Utilities for building and transforming URIs. */ public class URITransformer { - /** Removes query parameters from the given URI. */ public static URI removeQueryParameters(URI uri) { return buildUriFromParts( @@ -81,4 +80,21 @@ public static String encodeForQuery(String value) { public static String encode(String value) { return URLEncoder.encode(value, StandardCharsets.UTF_8).replace("+", "%20"); } + + public static String encodeQuery(String rawQueryString) { + var parts = rawQueryString.split("&"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parts.length; i++) { + if (i > 0) { + sb.append("&"); + } + var keyValue = parts[i].split("=", 2); + sb.append(encodeForQuery(keyValue[0])); + if (keyValue.length > 1) { + sb.append("="); + sb.append(encodeForQuery(keyValue[1])); + } + } + return sb.toString(); + } } diff --git a/test/Base_Tests/src/Network/URI_Spec.enso b/test/Base_Tests/src/Network/URI_Spec.enso index 31f5d944cf94..732c04738931 100644 --- a/test/Base_Tests/src/Network/URI_Spec.enso +++ b/test/Base_Tests/src/Network/URI_Spec.enso @@ -3,6 +3,7 @@ import Standard.Base.Enso_Cloud.Enso_Secret.Enso_Secret_Error import Standard.Base.Errors.Common.Syntax_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Network.HTTP.Request.Request +import Standard.Base.Network.URI.Invalid_Query_String from Standard.Test import all @@ -66,6 +67,15 @@ add_specs suite_builder = addr.raw_query.should_equal "%D0%9A%D0%BE%D0%B4" addr.raw_fragment.should_equal Nothing + group_builder.specify "should automatically URI encode the query string if needed with a warning" <| + addr = URI.parse 'https://httpbin.org/get?foo=bar&baz="a&c=d"%20?' + addr.to_text . should_equal "https://httpbin.org/get?foo=bar&baz=%22a&c=d%22%2520%3F" + w1 = Problems.expect_only_warning Invalid_Query_String addr + w1.query_string . should_equal 'foo=bar&baz="a&c=d"%20?' + + addr2 = URI.parse 'https://httpbin.org/get?c=d d=e=f&g=h' + addr2.to_text . should_equal 'https://httpbin.org/get?c=d+d%3De%3Df&g=h' + group_builder.specify "should return Syntax_Error when parsing invalid URI" <| r = URI.parse "a b c" r.should_fail_with Syntax_Error