From beff637d016e3c2b12ff683548f028cc2fee65d9 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 25 Sep 2024 09:39:27 +0100 Subject: [PATCH 01/10] WIP --- .../Standard/Base/0.0.0-dev/src/Data/Json.enso | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso index 8082f5451a60..c879ee59b0a9 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso @@ -29,7 +29,8 @@ from project.Data.Ordering import all from project.Data.Range.Extensions import all from project.Data.Text.Extensions import all from project.Metadata.Choice import Option -from project.Metadata.Widget import Single_Choice +from project.Metadata.Widget import Single_Choice, Text_Input, Vector_Editor +from project.Widget_Helpers import make_any_selector polyglot java import com.fasterxml.jackson.core.JsonLocation polyglot java import com.fasterxml.jackson.core.JsonProcessingException @@ -161,10 +162,11 @@ type JS_Object loop name_iterator builder JS_Object.Value object_node (make_field_names object_node) - ## PRIVATE - Creates a Jackson_Object from a list of key-value pairs. + ## ICON braces + Creates a JS_Object from a list of key-value pairs. Keys must be `Text` values. Values will be recursively converted to JSON serializable as needed. + @pairs key_value_widget from_pairs : Vector -> JS_Object from_pairs pairs = mapper = ObjectMapper.new @@ -423,3 +425,13 @@ make_enso object = _ -> js_object if parsed.is_error then js_object else parsed + +## PRIVATE +key_value_widget : Widget +key_value_widget = + value_editor = make_any_selector add_text=True add_number=True add_boolean=True add_date=True add_time=True add_date_time=True add_nothing=True + fqn = Meta.get_qualified_type_name Pair + default = 'Pair.Value "key" Nothing' + pair = Option "Pair" fqn+".Value" [["first", Text_Input], ["second", value_editor]] + item_editor = Single_Choice display=..Always values=[pair] + Vector_Editor item_editor=item_editor display=..Always item_default=default From 9bf2f82e394df7949a95d1ebf708e181609e7db7 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 25 Sep 2024 15:10:44 +0100 Subject: [PATCH 02/10] Initial widget work on JS_Object and Dictionary. --- .../Base/0.0.0-dev/src/Data/Dictionary.enso | 39 +++++++++++++++---- .../Base/0.0.0-dev/src/Data/Json.enso | 11 +++--- .../Base/0.0.0-dev/src/Data/Pair.enso | 9 +++++ .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 1 + .../Base/0.0.0-dev/src/Widget_Helpers.enso | 6 +++ 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso index 14fadddf3746..86d735f22b00 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso @@ -6,10 +6,15 @@ import project.Data.Vector.Vector import project.Error.Error import project.Errors.Illegal_Argument.Illegal_Argument import project.Errors.No_Such_Key.No_Such_Key +import project.Meta +import project.Metadata.Widget import project.Nothing.Nothing import project.Panic.Panic from project.Data.Boolean import Boolean, False, True from project.Data.Text.Extensions import all +from project.Metadata.Choice import Option +from project.Metadata.Widget import Single_Choice, Vector_Editor +from project.Widget_Helpers import make_all_selector ## A key-value store. It is possible to use any type as keys and values and mix them in one Dictionary. Keys are checked for equality based on their hash @@ -32,14 +37,12 @@ from project.Data.Text.Extensions import all to pass a foreign map into Enso, where it will be treated as a Dictionary. @Builtin_Type type Dictionary key value - ## PRIVATE - ADVANCED + ## ICON array_new2 Returns an empty dictionary. empty : Dictionary empty = @Builtin_Method "Dictionary.empty" - ## PRIVATE - ADVANCED + ## ICON array_new2 Returns a single-element dictionary with the given key and value. A Call to `Dictionary.singleton key value` is the same as a call to `Dictionary.empty.insert key value`. @@ -53,12 +56,14 @@ type Dictionary key value value 2. example_singleton = Dictionary.singleton "my_key" 2 + @key (make_all_selector ..Always) + @value (make_all_selector ..Always) singleton : Any -> Any -> Dictionary singleton key value = Dictionary.empty.insert key value ## ALIAS dictionary, lookup table GROUP Constants - ICON convert + ICON array_new2 Builds a dictionary from two Vectors. The first vector contains the keys, and the second vector contains the values. The two vectors must be of the same length. @@ -71,6 +76,8 @@ type Dictionary key value meaning that if two entries in the vector share the same key, an `Illegal_Argument` error is raised. If set to `False`, the last entry with a given key will be kept. + @keys (Vector_Editor item_editor=make_all_selector display=..Always item_default="Nothing") + @values (Vector_Editor item_editor=make_all_selector display=..Always item_default="Nothing") from_keys_and_values : Vector Any -> Vector Any -> Boolean -> Dictionary ! Illegal_Argument from_keys_and_values keys:Vector values:Vector error_on_duplicates:Boolean=True = if keys.length != values.length then Error.throw (Illegal_Argument.Error "`Dictionary.from_keys_and_values` encountered two vectors of different lengths.") else @@ -80,7 +87,7 @@ type Dictionary key value ## ALIAS dictionary, lookup table GROUP Constants - ICON convert + ICON array_new2 Builds a dictionary from a vector of key-value pairs, with each key-value pair represented as a 2 element vector. @@ -96,8 +103,9 @@ type Dictionary key value Building a dictionary containing two key-value pairs. example_from_vector = Dictionary.from_vector [["A", 1], ["B", 2]] + @vec key_value_widget from_vector : Vector Any -> Boolean -> Dictionary ! Illegal_Argument - from_vector vec error_on_duplicates=True = + from_vector vec error_on_duplicates:Boolean=True = vec.fold Dictionary.empty m-> el-> if el.length != 2 then Error.throw (Illegal_Argument.Error "`Dictionary.from_vector` encountered an invalid value. Each value in the vector has to be a key-value pair - it must have exactly 2 elements.") else key = el.at 0 value = el.at 1 @@ -152,6 +160,8 @@ type Dictionary key value import Standard.Examples example_insert = Examples.dictionary.insert 7 "seven" + @key (make_all_selector ..Always) + @value (make_all_selector ..Always) insert : Any -> Any -> Dictionary insert self key value = @Builtin_Method "Dictionary.insert" @@ -423,3 +433,18 @@ type Dictionary key value ## PRIVATE get_builtin : Any -> Any -> Any get_builtin self key ~if_missing = @Builtin_Method "Dictionary.get_builtin" + +## PRIVATE +key_widget Dictionary -> Widget +key_widget dict = + values = dict.keys.map k-> Option k.to_text k.pretty + Single_Choice display=..Always values=values + +## PRIVATE +key_value_widget : Widget +key_value_widget = + fqn = Meta.get_qualified_type_name Pair . drop (..Last 5) + default = 'pair "key" Nothing' + pair = Option "Pair" fqn+".pair" [["first", make_all_selector], ["second", make_all_selector]] + item_editor = Single_Choice display=..Always values=[pair] + Vector_Editor item_editor=item_editor display=..Always item_default=default diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso index c879ee59b0a9..982b5a06b7f1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso @@ -30,7 +30,7 @@ from project.Data.Range.Extensions import all from project.Data.Text.Extensions import all from project.Metadata.Choice import Option from project.Metadata.Widget import Single_Choice, Text_Input, Vector_Editor -from project.Widget_Helpers import make_any_selector +from project.Widget_Helpers import make_all_selector polyglot java import com.fasterxml.jackson.core.JsonLocation polyglot java import com.fasterxml.jackson.core.JsonProcessingException @@ -350,7 +350,7 @@ type JS_Object ## PRIVATE Make a field name selector make_field_name_selector : JS_Object -> Display -> Widget -make_field_name_selector js_object display=Display.Always = +make_field_name_selector js_object display:Display=..Always = Single_Choice display=display values=(js_object.field_names.map n->(Option n n.pretty)) ## PRIVATE @@ -429,9 +429,8 @@ make_enso object = ## PRIVATE key_value_widget : Widget key_value_widget = - value_editor = make_any_selector add_text=True add_number=True add_boolean=True add_date=True add_time=True add_date_time=True add_nothing=True - fqn = Meta.get_qualified_type_name Pair - default = 'Pair.Value "key" Nothing' - pair = Option "Pair" fqn+".Value" [["first", Text_Input], ["second", value_editor]] + fqn = Meta.get_qualified_type_name Pair . drop (..Last 5) + default = 'pair "key" Nothing' + pair = Option "Pair" fqn+".pair" [["first", Text_Input], ["second", make_all_selector]] item_editor = Single_Choice display=..Always values=[pair] Vector_Editor item_editor=item_editor display=..Always item_default=default diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Pair.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Pair.enso index 3ed19b9fe045..520c8f8ff173 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Pair.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Pair.enso @@ -295,3 +295,12 @@ check_start_valid start function max=3 = used_start = if start < 0 then start + 2 else start if used_start < 0 || used_start >= max then Error.throw (Index_Out_Of_Bounds.Error start max) else function used_start + +## ICON array_new + Create a new Pair from two elements. + + Arguments: + - first: The first element. + - second: The second element. +pair : Any -> Any -> Pair +pair first second -> Pair = Pair.new first second diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index c825197549b8..4dfbac3034a9 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -32,6 +32,7 @@ export project.Data.Numeric.Rounding_Mode.Rounding_Mode export project.Data.Ordering.Comparable export project.Data.Ordering.Natural_Order export project.Data.Ordering.Ordering +export project.Data.Pair.pair export project.Data.Pair.Pair export project.Data.Range.Extensions.down_to export project.Data.Range.Extensions.up_to diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Widget_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Widget_Helpers.enso index caa7fe7decf8..f8ee75da29a1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Widget_Helpers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Widget_Helpers.enso @@ -131,6 +131,12 @@ make_data_cleanse_vector_selector display:Display=Display.Always = options = patterns.map f-> Option f (".." + f) Widget.Multiple_Choice values=options display=display +## PRIVATE + Creates a Single_Choice Widget for Any selectors with all types enabled. +make_all_selector : Display -> Widget +make_all_selector display:Display=..Always -> Widget = + make_any_selector display add_text=True add_number=True add_boolean=True add_date=True add_time=True add_date_time=True add_nothing=True + ## PRIVATE Creates a Single_Choice Widget for Any selectors. make_any_selector : Display -> Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Widget From 0093588a9a45779880fae36a3cca0ca22f8bb09b Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Wed, 25 Sep 2024 17:11:46 +0100 Subject: [PATCH 03/10] Add pretty for Date and Time_Of_Day. Widgets for all of Dictionary. --- .../Standard/Base/0.0.0-dev/src/Data/Dictionary.enso | 10 ++++++---- .../Standard/Base/0.0.0-dev/src/Data/Time/Date.enso | 5 +++++ .../Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 12 ++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso index 86d735f22b00..cd4709b0d1a9 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso @@ -180,6 +180,7 @@ type Dictionary key value import Standard.Examples Examples.dictionary.remove "A" + @key key_widget remove : Any -> Dictionary ! No_Such_Key remove self key = Panic.catch Any (self.remove_builtin key) _-> @@ -201,6 +202,7 @@ type Dictionary key value import Standard.Examples example_at = Examples.dictionary.at "A" + @key key_widget at : Any -> Any ! No_Such_Key at self key = self.get key (Error.throw (No_Such_Key.Error self key)) @@ -221,12 +223,14 @@ type Dictionary key value import Standard.Examples example_get = Examples.dictionary.get 2 "zero" + @key key_widget get : Any -> Any -> Any get self key ~if_missing=Nothing = self.get_builtin key if_missing ## GROUP Logical ICON preparation Returns True iff the Dictionary contains the given `key`. + @key (make_all_selector ..Always) contains_key : Any -> Boolean contains_key self key = @Builtin_Method "Dictionary.contains_key" @@ -435,14 +439,12 @@ type Dictionary key value get_builtin self key ~if_missing = @Builtin_Method "Dictionary.get_builtin" ## PRIVATE -key_widget Dictionary -> Widget -key_widget dict = +key_widget dict:Dictionary -> Widget = values = dict.keys.map k-> Option k.to_text k.pretty Single_Choice display=..Always values=values ## PRIVATE -key_value_widget : Widget -key_value_widget = +key_value_widget -> Widget = fqn = Meta.get_qualified_type_name Pair . drop (..Last 5) default = 'pair "key" Nothing' pair = Option "Pair" fqn+".pair" [["first", make_all_selector], ["second", make_all_selector]] 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 94816f343e92..408aded58693 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 @@ -826,6 +826,11 @@ type Date format self format:Date_Time_Formatter=Date_Time_Formatter.iso_date = format.format_date self + ## PRIVATE + Convert to a Enso code representation of this Date. + pretty : Text + pretty self = "(Date.new " + self.year.to_text + " " + self.month.to_text + " " + self.day.to_text + ")" + ## PRIVATE week_days_between start end = ## We split the interval into 3 periods: the first week (containing the 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 7a18e695fdb6..fb380caf59ef 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 @@ -491,6 +491,18 @@ type Time_Of_Day format self format:Date_Time_Formatter = format.format_time self + ## PRIVATE + Convert to a Enso code representation of this Time_Of_Day. + pretty : Text + pretty self = "(Time_Of_Day.new " + + (if self.hour == 0 then "" else " hour="+self.hour.to_text) + + (if self.minute == 0 then "" else " minute="+self.minute.to_text) + + (if self.second == 0 then "" else " second="+self.second.to_text) + + (if self.millisecond == 0 then "" else " millisecond="+self.millisecond.to_text) + + (if self.microsecond == 0 then "" else " microsecond="+self.microsecond.to_text) + + (if self.nanosecond == 0 then "" else " nanosecond="+self.nanosecond.to_text) + + ")" + ## PRIVATE Time_Of_Day.from (that:JS_Object) = ## Must have hour and minute but second and nanosecond are optional From a34b24d2fe954cf1bdf56544cc3005d155bfad8f Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Thu, 26 Sep 2024 14:03:47 +0100 Subject: [PATCH 04/10] Rename insert to insert_builtin to make widgets work. --- .../lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso | 6 ++++-- .../interpreter/runtime/data/hash/HashMapInsertNode.java | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso index cd4709b0d1a9..c51a7c4c6810 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso @@ -4,6 +4,7 @@ import project.Data.Pair.Pair import project.Data.Text.Text import project.Data.Vector.Vector import project.Error.Error +import project.Errors.Common.Missing_Argument import project.Errors.Illegal_Argument.Illegal_Argument import project.Errors.No_Such_Key.No_Such_Key import project.Meta @@ -163,7 +164,8 @@ type Dictionary key value @key (make_all_selector ..Always) @value (make_all_selector ..Always) insert : Any -> Any -> Dictionary - insert self key value = @Builtin_Method "Dictionary.insert" + insert self key=(Missing_Argument.throw "key") value=(Missing_Argument.throw "value") = + self.insert_builtin key value ## GROUP Selections ICON table_clean @@ -182,7 +184,7 @@ type Dictionary key value Examples.dictionary.remove "A" @key key_widget remove : Any -> Dictionary ! No_Such_Key - remove self key = + remove self key=(Missing_Argument.throw "key") = Panic.catch Any (self.remove_builtin key) _-> Error.throw (No_Such_Key.Error self key) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/hash/HashMapInsertNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/hash/HashMapInsertNode.java index b73ad9d8dda0..d2cca32666e1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/hash/HashMapInsertNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/hash/HashMapInsertNode.java @@ -20,12 +20,11 @@ @BuiltinMethod( type = "Dictionary", - name = "insert", + name = "insert_builtin", description = """ Returns newly created hash map with the given key value mapping. - """, - autoRegister = false) + """) @GenerateUncached public abstract class HashMapInsertNode extends Node { From 754e871e6e9632f78a8a1d6da32328ab5ecfd767 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Thu, 26 Sep 2024 17:02:59 +0100 Subject: [PATCH 05/10] Add headers widget. --- .../lib/Standard/Base/0.0.0-dev/src/Data.enso | 6 ++++-- .../0.0.0-dev/src/Network/Extensions.enso | 5 +++-- .../Base/0.0.0-dev/src/Network/HTTP.enso | 4 +++- .../0.0.0-dev/src/Network/HTTP/Header.enso | 20 +++++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso index e17fb60fa865..5c8b1b3ddf9e 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso @@ -196,8 +196,9 @@ list (directory:(Text | File)=enso_project.root) (name_filter:Text="") recursive Data.fetch URL . body . write file @uri Text_Input @format Data_Read_Helpers.format_widget_with_raw_response +@headers Header.default_widget fetch : (URI | Text) -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any ! Request_Error | HTTP_Error -fetch (uri:(URI | Text)) (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector (Header | Pair Text Text))=[]) (format = Auto_Detect) = +fetch (uri:(URI | Text)) (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) (format = Auto_Detect) = Data_Read_Helpers.fetch_following_data_links uri method headers (Data_Read_Helpers.handle_legacy_format "fetch" "format" format) ## ALIAS http post, upload @@ -321,9 +322,10 @@ fetch (uri:(URI | Text)) (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector ( form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]] response = Data.post url_post (Request_Body.Form_Data form_data url_encoded=True) @uri Text_Input +@headers Header.default_widget @response_format Data_Read_Helpers.format_widget_with_raw_response post : (URI | Text) -> Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any ! Request_Error | HTTP_Error -post (uri:(URI | Text)) (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=HTTP_Method.Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = +post (uri:(URI | Text)) (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=..Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = response = HTTP.post uri body method headers Data_Read_Helpers.decode_http_response_following_data_links response (Data_Read_Helpers.handle_legacy_format "post" "response_format" response_format) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Extensions.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Extensions.enso index f116617483bf..09cfbd9ec69d 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Extensions.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Extensions.enso @@ -44,7 +44,7 @@ Text.to_uri self = URI.parse self cannot be determined automatically, a raw HTTP `Response` will be returned. @format Data_Read_Helpers.format_widget_with_raw_response URI.fetch : HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any -URI.fetch self (method:HTTP_Method=HTTP_Method.Get) headers=[] format=Auto_Detect = +URI.fetch self (method:HTTP_Method=..Get) headers=[] format=Auto_Detect = Data.fetch self method headers format ## ALIAS http post, upload @@ -85,7 +85,8 @@ URI.fetch self (method:HTTP_Method=HTTP_Method.Get) headers=[] format=Auto_Detec - Text: shorthand for `Request_Body.Text that_text`. - File: shorthand for `Request_Body.Binary that_file`. - Any other Enso object: shorthand for `Request_Body.Json that_object`. +@headers Header.default_widget @response_format Data_Read_Helpers.format_widget_with_raw_response URI.post : Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any -URI.post self (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=HTTP_Method.Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = +URI.post self (body:Request_Body=..Empty) (method:HTTP_Method=..Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = Data.post self body method headers response_format diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso index 62b19da77954..a2d9333bc23b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso @@ -11,6 +11,7 @@ import project.Enso_Cloud.Enso_Secret.Enso_Secret import project.Error.Error import project.Errors.Common.Forbidden_Operation import project.Errors.Illegal_Argument.Illegal_Argument +import project.Function.Function import project.Meta import project.Network.HTTP.Header.Header import project.Network.HTTP.HTTP_Error.HTTP_Error @@ -137,7 +138,7 @@ type HTTP ## PRIVATE Static helper for get-like methods fetch : (URI | Text) -> HTTP_Method -> Vector (Header | Pair Text Text) -> Response ! Request_Error | HTTP_Error - fetch (uri:(URI | Text)) (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector (Header | Pair Text Text))=[]) = + fetch (uri:(URI | Text)) (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) = check_method fetch_methods method <| request = Request.new method uri (parse_headers headers) Request_Body.Empty HTTP.new.request request @@ -187,6 +188,7 @@ parse_headers headers = headers . map on_problems=No_Wrap h-> case h of _ : Vector -> Header.new (h.at 0) (h.at 1) _ : Pair -> Header.new (h.at 0) (h.at 1) + _ : Function -> h:Header _ : Header -> h _ -> Error.throw (Illegal_Argument.Error "Invalid header type - all values must be Vector, Pair or Header (got "+(Meta.get_simple_type_name h)+").") diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso index ea77de513842..5ddbd94ff405 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso @@ -3,11 +3,18 @@ import project.Data.Text.Encoding.Encoding import project.Data.Text.Text import project.Enso_Cloud.Enso_Secret.Derived_Secret_Value import project.Enso_Cloud.Enso_Secret.Enso_Secret +import project.Meta +import project.Metadata.Display +import project.Metadata.Widget import project.Nothing.Nothing from project.Data.Boolean import Boolean, False, True from project.Data.Ordering import all from project.Data.Text.Extensions import all from project.Enso_Cloud.Enso_Secret import as_hideable_value +from project.Metadata import make_single_choice +from project.Metadata.Choice import Option +from project.Metadata.Widget import Single_Choice, Vector_Editor +from project.Widget_Helpers import make_text_secret_selector polyglot java import org.graalvm.collections.Pair as Java_Pair @@ -213,6 +220,19 @@ type Header to_java_pair self = Java_Pair.create self.name (as_hideable_value self.value) + ## PRIVATE + default_widget (display : Display = ..When_Modified) -> Widget = + fqn = "Header" + secret_selector = make_text_secret_selector + values = [Option "Custom" fqn+".new '' ''"] + + [Option "Accept" fqn+".accept '*/*'"] + + [Option "Authorization" fqn+".authorization ''" [["value", secret_selector]]] + + [Option "HTTP Basic Auth" fqn+".authorization_basic '' ''" [["user", secret_selector], ["pass", secret_selector]]] + + [Option "Bearer Auth" fqn+".authorization_bearer ''" [["token", secret_selector]]] + + [Option "Content-Type" fqn+".content_type ''" [["value", make_single_choice [["Custom", "*"], "application/json", "application/octet-stream", "application/x-www-form-urlencoded", "multipart/form-data", "text/csv", "text/html", "text/plain", "text/xml"]], ["encoding", Encoding.default_widget]]] + item_editor = Single_Choice values=values display=..Always + Vector_Editor item_editor item_default='Header.new "header" "value"' display=display + ## PRIVATE type Header_Comparator ## PRIVATE From 78440c2dcecfc8f2e7dd1fe0d79e948dc16a4840 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Fri, 27 Sep 2024 10:46:18 +0100 Subject: [PATCH 06/10] Update Request_Body to have widgets and be usable. --- .../lib/Standard/Base/0.0.0-dev/src/Data.enso | 2 +- .../Base/0.0.0-dev/src/Data/Dictionary.enso | 3 ++ .../0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso | 1 - .../src/Enso_Cloud/Internal/Utils.enso | 4 +- .../0.0.0-dev/src/Network/HTTP/Header.enso | 6 ++- .../0.0.0-dev/src/Network/HTTP/Request.enso | 2 +- .../src/Network/HTTP/Request_Body.enso | 37 +++++++++++++++++-- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso index 5c8b1b3ddf9e..2c85bd7075cf 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso @@ -325,7 +325,7 @@ fetch (uri:(URI | Text)) (method:HTTP_Method=..Get) (headers:(Vector (Header | P @headers Header.default_widget @response_format Data_Read_Helpers.format_widget_with_raw_response post : (URI | Text) -> Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any ! Request_Error | HTTP_Error -post (uri:(URI | Text)) (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=..Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = +post (uri:(URI | Text)) (body:Request_Body=..Empty) (method:HTTP_Method=..Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) = response = HTTP.post uri body method headers Data_Read_Helpers.decode_http_response_following_data_links response (Data_Read_Helpers.handle_legacy_format "post" "response_format" response_format) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso index c51a7c4c6810..07942e8924e4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Dictionary.enso @@ -452,3 +452,6 @@ key_value_widget -> Widget = pair = Option "Pair" fqn+".pair" [["first", make_all_selector], ["second", make_all_selector]] item_editor = Single_Choice display=..Always values=[pair] Vector_Editor item_editor=item_editor display=..Always item_default=default + +## PRIVATE +Dictionary.from (that:Vector) = Dictionary.from_vector that diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso index 79b7e8521892..94fffffb7822 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso @@ -13,7 +13,6 @@ import project.Errors.Common.Not_Found import project.Errors.Illegal_Argument.Illegal_Argument import project.Network.HTTP.HTTP import project.Network.HTTP.HTTP_Method.HTTP_Method -import project.Network.HTTP.Request_Body.Request_Body import project.Network.URI.URI import project.Nothing.Nothing import project.Runtime.Context diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Utils.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Utils.enso index 676846590108..fb877717b66f 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Utils.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Utils.enso @@ -72,7 +72,7 @@ flush_caches = CloudAPI.flushCloudCaches ## PRIVATE Performs a standard request to the Enso Cloud API, parsing the result as JSON. -http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Any ! Enso_Cloud_Error = +http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = ..Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Any ! Enso_Cloud_Error = response = http_request method url body additional_headers error_handlers retries response.decode_as_json.catch Invalid_JSON error-> Error.throw (Enso_Cloud_Error.Invalid_Response_Payload error) @@ -87,7 +87,7 @@ http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = R Custom error handlers can be provided as a mapping from error codes (defined in the cloud project) to functions that take the full JSON payload and return a custom error. -http_request (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Response ! Enso_Cloud_Error = method.if_not_error <| url.if_not_error <| body.if_not_error <| additional_headers.if_not_error <| +http_request (method : HTTP_Method) (url : URI) (body : Request_Body = ..Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Response ! Enso_Cloud_Error = method.if_not_error <| url.if_not_error <| body.if_not_error <| additional_headers.if_not_error <| all_headers = [authorization_header] + additional_headers as_connection_error err = Error.throw (Enso_Cloud_Error.Connection_Error err) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso index 5ddbd94ff405..35944ff5897f 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso @@ -229,7 +229,7 @@ type Header [Option "Authorization" fqn+".authorization ''" [["value", secret_selector]]] + [Option "HTTP Basic Auth" fqn+".authorization_basic '' ''" [["user", secret_selector], ["pass", secret_selector]]] + [Option "Bearer Auth" fqn+".authorization_bearer ''" [["token", secret_selector]]] + - [Option "Content-Type" fqn+".content_type ''" [["value", make_single_choice [["Custom", "*"], "application/json", "application/octet-stream", "application/x-www-form-urlencoded", "multipart/form-data", "text/csv", "text/html", "text/plain", "text/xml"]], ["encoding", Encoding.default_widget]]] + [Option "Content-Type" fqn+".content_type ''" [["value", content_type_widget], ["encoding", Encoding.default_widget]]] item_editor = Single_Choice values=values display=..Always Vector_Editor item_editor item_default='Header.new "header" "value"' display=display @@ -249,3 +249,7 @@ type Header_Comparator ## PRIVATE Comparable.from (that:Header) = Comparable.new that Header_Comparator + +## PRIVATE +content_type_widget -> Widget = + make_single_choice [["Custom", "*"], "application/json", "application/octet-stream", "application/x-www-form-urlencoded", "multipart/form-data", "text/csv", "text/html", "text/plain", "text/xml"] diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request.enso index c282eef39b7f..6104c43ed0a2 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request.enso @@ -34,7 +34,7 @@ type Request example_new = Request.new Method.Post (URI.parse "http://example.com") new : HTTP_Method -> URI -> Vector Header -> Request_Body -> Request - new (method:HTTP_Method) (url:URI) (headers:(Vector Header)=[]) (body:Request_Body=Request_Body.Empty) = + new (method:HTTP_Method) (url:URI) (headers:(Vector Header)=[]) (body:Request_Body=..Empty) = Request.Value method url headers body ## ICON find diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request_Body.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request_Body.enso index 84a42d25d9a6..1a0e8553a7b8 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request_Body.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Request_Body.enso @@ -1,11 +1,21 @@ import project.Any.Any import project.Data.Dictionary.Dictionary +import project.Data.Pair.Pair import project.Data.Text.Encoding.Encoding import project.Data.Text.Text +import project.Errors.Common.Missing_Argument +import project.Meta +import project.Metadata.Widget import project.Network.HTTP.Header.Header import project.Nothing.Nothing import project.System.File.File from project.Data.Boolean import Boolean, False, True +from project.Data.Json import key_value_widget +from project.Data.Text.Extensions import all +from project.Metadata.Choice import Option +from project.Metadata.Widget import File_Browse, Single_Choice, Text_Input, Vector_Editor +from project.Network.HTTP.Header import content_type_widget +from project.Widget_Helpers import make_all_selector ## The HTTP POST request body. type Request_Body @@ -16,19 +26,22 @@ type Request_Body - text: The plain text in the request body. - encoding: the text encoding to send as a Content-Encoding header - content_type: the content_type to send as a Content-Type header - Text (text:Text) (encoding:(Encoding|Nothing)=Nothing) (content_type:(Text|Nothing)=Nothing) + @encoding Encoding.default_widget + @content_type content_type_widget + Text (text:Text="") (encoding:(Encoding|Nothing)=Nothing) (content_type:(Text|Nothing)=Nothing) ## Request body with an object to be sent as JSON. Arguments: - x: The object to convert to JSON using `.to_json`. - Json (x:Any) + @x make_all_with_json + Json (x:Any=(Missing_Argument.throw "x")) ## Request body with an object to be sent as a binary file. Arguments: - file: The file to send. - Binary (file:File) + Binary (file:File=(Missing_Argument.throw "file")) ## Request body with form data. @@ -36,7 +49,8 @@ type Request_Body - form_data: the form fields (text or file) to be sent - url_encoded: if true, use a URL-encoded form; otherwise, use a multi-part encoding. - Form_Data (form_data:(Dictionary Text (Text | File))) (url_encoded:Boolean=False) + @form_data dictionary_widget + Form_Data (form_data:Dictionary=(Missing_Argument.throw "form_data")) (url_encoded:Boolean=False) ## Empty request body; used for GET Empty @@ -61,3 +75,18 @@ Request_Body.from (that:File) = Request_Body.Binary that ## PRIVATE Request_Body.from (that:Any) = Request_Body.Json that + +## PRIVATE +make_all_with_json = + base_selector = make_all_selector Request_Body + values = [Option "JSON Object" key_value_widget] + base_selector.values + Single_Choice display=..Always values=values + +## PRIVATE +dictionary_widget -> Widget = + fqn = Meta.get_qualified_type_name Pair . drop (..Last 5) + default = 'pair "key" ""' + value_editor = Single_Choice display=..Always values=[Option "Text" '""', Option "File" "File.new ''"] + pair = Option "Pair" fqn+".pair" [["first", Text_Input], ["second", value_editor]] + item_editor = Single_Choice display=..Always values=[pair] + Vector_Editor item_editor=item_editor display=..Always item_default=default From 1283888ca0836736fbf7861bd1d336391e382b5b Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Fri, 27 Sep 2024 13:24:57 +0100 Subject: [PATCH 07/10] Forbidden messages. --- .../0.0.0-dev/src/Internal/S3_File_Write_Strategy.enso | 2 +- .../lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso | 8 ++++---- distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso | 5 +++-- .../Base/0.0.0-dev/src/Enso_Cloud/Enso_File.enso | 2 +- .../Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso | 6 +++--- .../Enso_Cloud/Internal/Enso_File_Write_Strategy.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/System/File.enso | 10 +++++----- .../Database/0.0.0-dev/src/Connection/Connection.enso | 4 ++-- .../Internal/Postgres/Postgres_Data_Link_Setup.enso | 2 +- .../lib/Standard/Image/0.0.0-dev/src/Image.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso | 2 +- 13 files changed, 25 insertions(+), 24 deletions(-) diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/Internal/S3_File_Write_Strategy.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/Internal/S3_File_Write_Strategy.enso index 2fed38a426f7..974a8496ab29 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/Internal/S3_File_Write_Strategy.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/Internal/S3_File_Write_Strategy.enso @@ -16,7 +16,7 @@ instance = ## PRIVATE create_dry_run_file file copy_original = _ = [file, copy_original] - Error.throw (Forbidden_Operation.Error "Currently dry-run is not supported for S3_File, so writing to an S3_File is forbidden if the Output context is disabled.") + Error.throw (Forbidden_Operation.Error "As writing is disabled, no action has been performed. Press the Write button ▶ to write the file.") ## PRIVATE remote_write_with_local_file file existing_file_behavior action = diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso index bdbf7225b745..171c48b13949 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso @@ -67,7 +67,7 @@ type S3_File value. The value is returned from this method. with_output_stream : Vector File_Access -> (Output_Stream -> Any ! File_Error) -> Any ! File_Error with_output_stream self (open_options : Vector) action = if self.is_directory then Error.throw (S3_Error.Error "S3 directory cannot be opened as a stream." self.uri) else - Context.Output.if_enabled disabled_message="Writing to an S3_File is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot write to S3. Press the Write button ▶ to perform the operation." panic=False <| open_as_data_link = (open_options.contains Data_Link_Access.No_Follow . not) && (Data_Link.is_data_link self) if open_as_data_link then Data_Link_Helpers.write_data_link_as_stream self open_options action else if open_options.contains File_Access.Append then Error.throw (S3_Error.Error "S3 does not support appending to a file. Instead you may read it, modify and then write the new contents." self.uri) else @@ -251,7 +251,7 @@ type S3_File copy_to : File_Like -> Boolean -> Any ! S3_Error | File_Error copy_to self (destination : File_Like) (replace_existing : Boolean = False) = Data_Link_Helpers.disallow_links_in_copy self destination <| if self.is_directory then Error.throw (S3_Error.Error "Copying S3 folders is currently not implemented." self.uri) else - Context.Output.if_enabled disabled_message="Copying an S3_File is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot copy to a file. Press the Write button ▶ to perform the operation." panic=False <| destination_writable = Writable_File.from destination case destination_writable.file of # Special shortcut for more efficient handling of S3 file copying (no need to move the data to our machine) @@ -292,7 +292,7 @@ type S3_File move_to self (destination : File_Like) (replace_existing : Boolean = False) = if self.is_directory then Error.throw (S3_Error.Error "Moving S3 folders is currently not implemented." self.uri) else Data_Link_Helpers.disallow_links_in_move self destination <| - Context.Output.if_enabled disabled_message="File moving is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot move the file. Press the Write button ▶ to perform the operation." panic=False <| r = self.copy_to destination replace_existing=replace_existing r.if_not_error <| # If source and destination are the same, we do not want to delete the file @@ -341,7 +341,7 @@ type S3_File will occur. delete_if_exists : Boolean -> Nothing delete_if_exists self (recursive : Boolean = False) = - Context.Output.if_enabled disabled_message="Deleting an S3_File is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot delete the file. Press the Write button ▶ to perform the operation." panic=False <| case self.is_directory of True -> # This is a temporary simplified implementation to ensure cleaning up after tests diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso index 2c85bd7075cf..aef2dd16efb1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso @@ -342,9 +342,10 @@ post (uri:(URI | Text)) (body:Request_Body=..Empty) (method:HTTP_Method=..Post) Defaults to `HTTP_Method.Get`. - headers: The headers to send with the request. Defaults to an empty vector. @uri Text_Input +@headers Header.default_widget download : (URI | Text) -> Writable_File -> HTTP_Method -> Vector (Header | Pair Text Text) -> File ! Request_Error | HTTP_Error -download (uri:(URI | Text)) file:Writable_File (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector (Header | Pair Text Text))=[]) = - Context.Output.if_enabled disabled_message="Downloading to a file is forbidden as the Output context is disabled." panic=False <| +download (uri:(URI | Text)) file:Writable_File (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) = + Context.Output.if_enabled disabled_message="As writing is disabled, cannot download to a file. Press the Write button ▶ to perform the operation." panic=False <| response = HTTP.fetch uri method headers case Data_Link.is_data_link response.body.metadata of True -> diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_File.enso index dc995bf71b3f..174cf26c7620 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_File.enso @@ -211,7 +211,7 @@ type Enso_File value. The value is returned from this method. with_output_stream : Vector File_Access -> (Output_Stream -> Any ! File_Error) -> Any ! File_Error with_output_stream self (open_options : Vector) action = - Context.Output.if_enabled disabled_message="Writing to an Enso_File is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot write to a file. Press the Write button ▶ to perform the operation." panic=False <| is_data_link = Data_Link.is_data_link self open_as_data_link = (open_options.contains Data_Link_Access.No_Follow . not) && is_data_link if open_as_data_link then Data_Link_Helpers.write_data_link_as_stream self open_options action else diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso index 94fffffb7822..538dface7dc4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Enso_Secret.enso @@ -44,7 +44,7 @@ type Enso_Secret created in the current working directory. create : Text -> Text -> Enso_File | Nothing -> Enso_Secret create name:Text value:Text parent:(Enso_File | Nothing)=Nothing = if name == "" then Error.throw (Illegal_Argument.Error "Secret name cannot be empty") else - Context.Output.if_enabled disabled_message="Creating a secret is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot create a secret. Press the Write button ▶ to perform the operation." panic=False <| if name.starts_with "connection-" then Error.throw (Illegal_Argument.Error "Secret name cannot start with 'connection-'") else parent_dir = parent.if_nothing Enso_File.current_working_directory path = if name.contains "/" then Error.throw (Illegal_Argument.Error "Secret name cannot contain `/`.") else parent_dir.enso_path.resolve name @@ -64,7 +64,7 @@ type Enso_Secret Deletes a secret. delete : Enso_Secret delete self = - Context.Output.if_enabled disabled_message="Deleting a secret is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot delete secret. Press the Write button ▶ to perform the operation." panic=False <| uri = URI.from (secret_asset_uri self) . add_query_argument "force" "true" response = Utils.http_request HTTP_Method.Delete uri response.if_not_error self @@ -139,7 +139,7 @@ type Enso_Secret - new_value: The new value of the secret update_value : Text -> Enso_Secret update_value self (new_value : Text) = - Context.Output.if_enabled disabled_message="Updating a secret is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot update secret. Press the Write button ▶ to perform the operation." panic=False <| body = JS_Object.from_pairs [["value", new_value]] response = Utils.http_request HTTP_Method.Put (secret_resource_uri self) body response.if_not_error <| diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Enso_File_Write_Strategy.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Enso_File_Write_Strategy.enso index f9c61122003d..ed55a65b40a4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Enso_File_Write_Strategy.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Enso_File_Write_Strategy.enso @@ -22,7 +22,7 @@ instance = ## PRIVATE create_dry_run_file file copy_original = _ = [file, copy_original] - Error.throw (Forbidden_Operation.Error "Currently dry-run is not supported for Enso_File, so writing to an Enso_File is forbidden if the Output context is disabled.") + Error.throw (Forbidden_Operation.Error "As writing is disabled, no action has been performed. Press the Write button ▶ to write the file.") ## PRIVATE diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso index d78531b42214..2b8d5b2c0bd8 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso @@ -431,7 +431,7 @@ type Forbidden_Operation ## PRIVATE Convert the Forbidden_Operation error to a human-readable format. to_display_text : Text - to_display_text self = "Forbidden operation: "+self.message + to_display_text self = self.message type Dry_Run_Operation ## PRIVATE diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso index a2d9333bc23b..8246493c1784 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP.enso @@ -107,7 +107,7 @@ type HTTP # Prevent request if the method is a write-like method and output context is disabled. check_output_context ~action = if fetch_methods.contains req.method || Context.Output.is_enabled then action else - Error.throw (Forbidden_Operation.Error ("Method " + req.method.to_text + " requests are forbidden as the Output context is disabled.")) + Error.throw (Forbidden_Operation.Error ("As writing is disabled, " + req.method.to_text + " request not sent. Press the Write button ▶ to send it.")) handle_request_error = handler caught_panic = exception = caught_panic.payload diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index fab85a043fa1..71a3237428f3 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -195,7 +195,7 @@ type File _ -> stream Output_Stream.new wrapped (File_Error.handle_java_exceptions self) - Context.Output.if_enabled disabled_message="File writing is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot write to a file. Press the Write button ▶ to perform the operation." panic=False <| open_as_data_link = (open_options.contains Data_Link_Access.No_Follow . not) && (Data_Link.is_data_link self) if open_as_data_link then Data_Link_Helpers.write_data_link_as_stream self open_options action else # We ignore the Data_Link_Access options at this stage: @@ -515,7 +515,7 @@ type File (Examples.data_dir / "my_directory") . create_directory create_directory : File ! File_Error create_directory self = - Context.Output.if_enabled disabled_message="Directory creation is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot create directory. Press the Write button ▶ to perform the operation." panic=False <| File_Error.handle_java_exceptions self self.create_directory_builtin . if_not_error self ## PRIVATE @@ -665,7 +665,7 @@ type File file.delete delete : Boolean -> Nothing ! File_Error delete self (recursive : Boolean = False) -> Nothing ! File_Error = - Context.Output.if_enabled disabled_message="File deleting is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot delete file. Press the Write button ▶ to perform the operation." panic=False <| File_Error.handle_java_exceptions self (self.delete_builtin recursive) ## ICON data_output @@ -677,7 +677,7 @@ type File destination file already exists. Defaults to `False`. copy_to : File_Like -> Boolean -> Any ! File_Error copy_to self (destination : File_Like) (replace_existing : Boolean = False) = Data_Link_Helpers.disallow_links_in_copy self destination <| - Context.Output.if_enabled disabled_message="File copying is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot copy file. Press the Write button ▶ to perform the operation." panic=False <| ## We defer the `Writable_File` conversion after the 'disallow links' check, because the conversion would already start resolving the data link too soon. destination_writable = Writable_File.from destination @@ -695,7 +695,7 @@ type File destination file already exists. Defaults to `False`. move_to : File_Like -> Boolean -> Any ! File_Error move_to self (destination : File_Like) (replace_existing : Boolean = False) = Data_Link_Helpers.disallow_links_in_move self destination <| - Context.Output.if_enabled disabled_message="File moving is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot move file. Press the Write button ▶ to perform the operation." panic=False <| ## We defer the `Writable_File` conversion after the 'disallow links' check, because the conversion would already start resolving the data link too soon. destination_writable = Writable_File.from destination diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso index 8bab13a4cff8..26f8157504c1 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso @@ -360,7 +360,7 @@ type Connection representing the query to execute. execute_update : Text | SQL_Statement -> Integer execute_update self query = - Execution_Context.Output.if_enabled disabled_message="Executing update queries is forbidden as the Output context is disabled." panic=False <| + Execution_Context.Output.if_enabled disabled_message="As writing is disabled, cannot execute an update query. Press the Write button ▶ to perform the operation." panic=False <| statement_setter = self.dialect.get_statement_setter self.jdbc_connection.with_prepared_statement query statement_setter stmt-> check_statement_is_allowed self stmt @@ -384,7 +384,7 @@ type Connection representing the query to execute. execute : Text | SQL_Statement -> Integer execute self query = - Execution_Context.Output.if_enabled disabled_message="Executing update queries is forbidden as the Output context is disabled." panic=False <| + Execution_Context.Output.if_enabled disabled_message="As writing is disabled, cannot execute an update query. Press the Write button ▶ to perform the operation." panic=False <| result = self.jdbc_connection.execute query stmt = result.second check_statement_is_allowed self stmt diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Data_Link_Setup.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Data_Link_Setup.enso index 9b0fd98d0c8e..7f9f1376d904 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Data_Link_Setup.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Data_Link_Setup.enso @@ -21,7 +21,7 @@ type Postgres_Data_Link_Setup ## PRIVATE save_as_data_link self destination on_existing_file:Existing_File_Behavior = case self of - Postgres_Data_Link_Setup.Available details -> Context.Output.if_enabled disabled_message="Saving a connection to data link requires the Output context." panic=False <| + Postgres_Data_Link_Setup.Available details -> Context.Output.if_enabled disabled_message="As writing is disabled, cannot save to a Data Link. Press the Write button ▶ to perform the operation." panic=False <| case destination of _ : Enso_File -> replace_existing = case on_existing_file of diff --git a/distribution/lib/Standard/Image/0.0.0-dev/src/Image.enso b/distribution/lib/Standard/Image/0.0.0-dev/src/Image.enso index 8c3811884a7f..89001fb78aa3 100644 --- a/distribution/lib/Standard/Image/0.0.0-dev/src/Image.enso +++ b/distribution/lib/Standard/Image/0.0.0-dev/src/Image.enso @@ -101,7 +101,7 @@ type Image @path (Widget.Text_Input display=Display.Always) write : Writable_File -> (Write_Flag | Vector) -> Nothing ! File_Error write self path:Writable_File flags=[] = - Context.Output.if_enabled disabled_message="Writing the image to a file is forbidden as the Output context is disabled." panic=False <| + Context.Output.if_enabled disabled_message="As writing is disabled, cannot write to a file. Press the Write button ▶ to perform the operation." panic=False <| write_flags = case flags of _ : Vector -> flags _ -> [flags] diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso index 7e4a6ce666de..07e9a99ed7fe 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso @@ -84,7 +84,7 @@ type Arity_Error type Forbidden_Operation Error message - to_display_text self = "Forbidden operation: "+self.message + to_display_text self = self.message ## PRIVATE A helper that replaces complicated `Text.replace` from main distribution, used for simple error message formatting. From 42c65b3e54914c2e7f05bb2cae298d61c3b73547 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Fri, 27 Sep 2024 16:20:57 +0100 Subject: [PATCH 08/10] Fix Header widget. --- .../Base/0.0.0-dev/src/Network/HTTP/Header.enso | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso index 35944ff5897f..4595256f8e08 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/HTTP/Header.enso @@ -224,12 +224,15 @@ type Header default_widget (display : Display = ..When_Modified) -> Widget = fqn = "Header" secret_selector = make_text_secret_selector - values = [Option "Custom" fqn+".new '' ''"] + - [Option "Accept" fqn+".accept '*/*'"] + - [Option "Authorization" fqn+".authorization ''" [["value", secret_selector]]] + - [Option "HTTP Basic Auth" fqn+".authorization_basic '' ''" [["user", secret_selector], ["pass", secret_selector]]] + - [Option "Bearer Auth" fqn+".authorization_bearer ''" [["token", secret_selector]]] + - [Option "Content-Type" fqn+".content_type ''" [["value", content_type_widget], ["encoding", Encoding.default_widget]]] + + custom = [Option "Custom" fqn+".new '' ''"] + accept = [Option "Accept" fqn+".accept '*/*'"] + authorization = [Option "Authorization" fqn+".authorization ''" [["value", secret_selector]]] + basic_auth = [Option "HTTP Basic Auth" fqn+".authorization_basic '' ''" [["user", secret_selector], ["pass", secret_selector]]] + bearer_auth = [Option "Bearer Auth" fqn+".authorization_bearer ''" [["token", secret_selector]]] + content_type = [Option "Content-Type" fqn+".content_type ''" [["value", content_type_widget], ["encoding", Encoding.default_widget]]] + values = custom + accept + authorization + basic_auth + bearer_auth + content_type + item_editor = Single_Choice values=values display=..Always Vector_Editor item_editor item_default='Header.new "header" "value"' display=display From 719bf5a103d8a1bfdad498161c67a836262498c3 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Fri, 27 Sep 2024 17:33:10 +0100 Subject: [PATCH 09/10] Fix failing test. --- .../test/instrument/RuntimeExecutionEnvironmentTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala index 9208cc906e94..4862d3b18bed 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala @@ -188,7 +188,7 @@ class RuntimeExecutionEnvironmentTest IF_ENABLED_METH_CALL, Api.ExpressionUpdate.Payload .Panic( - "Forbidden operation: The Output context is disabled.", + "The Output context is disabled.", Seq(idRes) ), false From 2580bb8b32794d9686944eee8be066e01cb76e1e Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Fri, 27 Sep 2024 18:11:23 +0100 Subject: [PATCH 10/10] Fix failing test. --- .../test/instrument/RuntimeExecutionEnvironmentTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala index 4862d3b18bed..38e122949286 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeExecutionEnvironmentTest.scala @@ -292,7 +292,7 @@ class RuntimeExecutionEnvironmentTest IF_ENABLED_METH_CALL, Api.ExpressionUpdate.Payload .Panic( - "Forbidden operation: The Input context is disabled.", + "The Input context is disabled.", Seq(idRes) ), false