From 0e9821519dc375f8e3fea2abd23ac64ae56fdfa0 Mon Sep 17 00:00:00 2001 From: James Dunkerley Date: Mon, 16 Sep 2024 19:56:14 +0100 Subject: [PATCH] Widget work for AWS (#11095) - Added `to_display_text` for `S3_File` and `Enso_File`. - Improved widget for `AWS_Credential` allowing use of Enso secrets. - Adjust `S3.list_objects` to return `S3_File` objects, allowing easier drill down. - Fix for merging inherited config with direct config in widgets. - Add missing constant types to Date.Diff widget. ![image](https://github.com/user-attachments/assets/ea125a09-5067-4dee-bef2-3d7c8d551260) --- .../providers/widgetRegistry/configuration.ts | 2 +- .../AWS/0.0.0-dev/src/AWS_Credential.enso | 18 ++++++++++++++++-- .../lib/Standard/AWS/0.0.0-dev/src/S3/S3.enso | 14 ++++++++++---- .../Standard/AWS/0.0.0-dev/src/S3/S3_File.enso | 11 +++++++++++ .../0.0.0-dev/src/Enso_Cloud/Enso_File.enso | 5 +++++ .../Table/0.0.0-dev/src/Simple_Expression.enso | 4 ++-- test/AWS_Tests/src/S3_Spec.enso | 2 +- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/gui2/src/providers/widgetRegistry/configuration.ts b/app/gui2/src/providers/widgetRegistry/configuration.ts index 323b939e7b..84cc30d71c 100644 --- a/app/gui2/src/providers/widgetRegistry/configuration.ts +++ b/app/gui2/src/providers/widgetRegistry/configuration.ts @@ -202,7 +202,7 @@ export function functionCallConfiguration( ): FunctionCall { const parametersMap = new Map(inherited?.parameters) for (const [name, param] of parameters) { - parametersMap.set(name, param) + parametersMap.set(name, parametersMap.get(name) ?? param) } return { kind: 'FunctionCall', diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/AWS_Credential.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/AWS_Credential.enso index 4fdf22ca2b..3a81417216 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/AWS_Credential.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/AWS_Credential.enso @@ -1,7 +1,10 @@ from Standard.Base import all import Standard.Base.Errors.Illegal_Argument.Illegal_Argument +import Standard.Base.Errors.Common.Missing_Argument from Standard.Base.Enso_Cloud.Enso_Secret import as_hideable_value -from Standard.Base.Metadata import make_single_choice, Widget +from Standard.Base.Metadata import Display, make_single_choice, Widget +from Standard.Base.Metadata.Choice import Option +from Standard.Base.Widget_Helpers import make_text_secret_selector import project.AWS_Region.AWS_Region @@ -25,7 +28,9 @@ type AWS_Credential Arguments: - access_key_id: AWS access key ID. - secret_access_key: AWS secret access key. - Key access_key_id:Text|Enso_Secret secret_access_key:Text|Enso_Secret + @access_key_id make_text_secret_selector + @secret_access_key make_text_secret_selector + Key (access_key_id : (Text|Enso_Secret) = (Missing_Argument.throw "access_key_id")) (secret_access_key : (Text|Enso_Secret) = (Missing_Argument.throw "secret_access_key")) ## PRIVATE Allows to override additional configuration associated with the credential. @@ -35,6 +40,7 @@ type AWS_Credential Default, Profile, Key. - default_region: The default region to use for operations that may require a region but it is not explicitly specified. + @default_region AWS_Region.default_widget With_Configuration (base_credential : AWS_Credential) (default_region : AWS_Region) ## ICON cloud @@ -106,3 +112,11 @@ type AWS_Credential ## PRIVATE to_display_text self -> Text = self.to_text.to_display_text + + ## PRIVATE + default_widget (display : Display = ..When_Modified) -> Widget = + default = Option "Default" "..Default" + profile = Option "Profile" "..Profile" [["profile", make_single_choice AWS_Credential.profile_names]] + key = Option "Key" "..Key" [["access_key_id", make_text_secret_selector], ["secret_access_key", make_text_secret_selector]] + with_config = Option "With_Configuration" "..With_Configuration" + Widget.Single_Choice values=[default, profile, key, with_config] display=display diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3.enso index d307d3c2bd..5f5d8a22a5 100644 --- a/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3.enso +++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3.enso @@ -1,10 +1,12 @@ from Standard.Base import all +import Standard.Base.Errors.Common.Missing_Argument import Standard.Base.Network.HTTP.Response_Body.Response_Body import Standard.Base.System.File_Format_Metadata.File_Format_Metadata import Standard.Base.System.Input_Stream.Input_Stream from Standard.Base.Network.HTTP.Response import filename_from_content_disposition import project.AWS_Credential.AWS_Credential +import project.S3.S3_File.S3_File import project.Errors.AWS_SDK_Error import project.Errors.More_Records_Available import project.Errors.S3_Bucket_Not_Found @@ -42,6 +44,7 @@ polyglot java import software.amazon.awssdk.services.s3.S3Client ! Error Conditions - If the credentials are invalid or access to S3 is denied, then an `AWS_SDK_Error` will be raised. +@credentials AWS_Credential.default_widget list_buckets : AWS_Credential -> Vector Text ! S3_Error list_buckets credentials:AWS_Credential=..Default = handle_s3_errors <| client = make_client credentials @@ -68,9 +71,11 @@ list_buckets credentials:AWS_Credential=..Default = handle_s3_errors <| - If the bucket does not exist, an `S3_Bucket_Not_Found` error is thrown. - If more items are available than the `max_count` parameter, a `More_Records_Available` warning is attached to the result. -list_objects : Text -> Text -> AWS_Credential -> Integer -> Vector Text ! S3_Error -list_objects bucket prefix="" credentials:AWS_Credential=..Default max_count:Integer=1000 = - read_bucket bucket prefix credentials delimiter="" max_count=max_count . second +@credentials AWS_Credential.default_widget +list_objects : Text -> Text -> AWS_Credential -> Integer -> Vector S3_File ! S3_Error +list_objects (bucket : Text = Missing_Argument.throw "bucket") prefix:Text="" credentials:AWS_Credential=..Default max_count:Integer=1000 = + names = read_bucket bucket prefix credentials delimiter="" max_count=max_count . second + names.map name-> S3_File.new "s3://"+bucket+"/"+name credentials ## PRIVATE ADVANCED @@ -121,8 +126,9 @@ get_object bucket key credentials:AWS_Credential=AWS_Credential.Default delimite `AWS_SDK_Error` will be raised. - If the bucket does not exist, an `S3_Bucket_Not_Found` error is thrown. - If the object does not exist, an `S3_Key_Not_Found` error is thrown. +@credentials AWS_Credential.default_widget head : Text -> Text -> AWS_Credential -> Dictionary Text Any ! S3_Error -head bucket key="" credentials:AWS_Credential=AWS_Credential.Default = +head (bucket : Text = Missing_Argument.throw "bucket") key:Text="" credentials:AWS_Credential=AWS_Credential.Default = response = raw_head bucket key credentials pairs = response.sdkFields.map f-> [f.memberName, f.getValueOrDefault response] Dictionary.from_vector pairs 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 e8243bf2ab..bdbf7225b7 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 @@ -44,6 +44,7 @@ type S3_File ! Error Conditions - If the URI is not in the correct format, an `Illegal_Argument` error is thrown. + @credentials AWS_Credential.default_widget new : Text -> AWS_Credential -> S3_File ! Illegal_Argument new (uri : Text = S3.uri_prefix) credentials:AWS_Credential=..Default = S3_File.Value (S3_Path.parse uri) credentials @@ -588,6 +589,16 @@ type S3_File is_descendant_of : S3_File -> Boolean is_descendant_of self other = self.s3_path.is_descendant_of other.s3_path + ## PRIVATE + Return the absolute path of this S3_File. + to_text : Text + to_text self = self.uri + + ## PRIVATE + Convert to a display representation of this S3_File. + to_display_text : Text + to_display_text self = "S3_File {" + self.to_text + "}" + ## PRIVATE File_Format_Metadata.from (that : S3_File) = File_Format_Metadata.Value that.uri that.name (that.extension.catch _->Nothing) 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 e4db670abb..dc995bf71b 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 @@ -191,6 +191,11 @@ type Enso_File is_descendant_of self (other : Enso_File) -> Boolean = self.enso_path.is_descendant_of other.enso_path + ## PRIVATE + Convert to a display representation of this S3_File. + to_display_text : Text + to_display_text self = "Enso_File {" + self.path + "}" + ## PRIVATE ADVANCED Creates a new output stream for this file and runs the specified action diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Simple_Expression.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Simple_Expression.enso index 0262eee9b6..a2247b4580 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Simple_Expression.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Simple_Expression.enso @@ -246,13 +246,13 @@ type Date_Operation ## PRIVATE create_widget : Table_Ref -> Display -> Widget create_widget table:Table_Ref display:Display=Display.Always = - col_names = Widget_Helpers.make_column_ref_by_name_selector table with_number = Widget_Helpers.make_column_ref_by_name_selector table add_number=True + with_date = Widget_Helpers.make_column_ref_by_name_selector table add_date=True add_date_time=True options = Vector.build builder-> builder.append (Option "add" "..Add" [["length", with_number]]) builder.append (Option "part" "..Part") - builder.append (Option "diff" "..Diff" [["end", col_names]]) + builder.append (Option "diff" "..Diff" [["end", with_date]]) builder.append (Option "truncate" "..Truncate") builder.append (Option "year" "..Year") builder.append (Option "month" "..Month") diff --git a/test/AWS_Tests/src/S3_Spec.enso b/test/AWS_Tests/src/S3_Spec.enso index 14b5ee1402..771505e79e 100644 --- a/test/AWS_Tests/src/S3_Spec.enso +++ b/test/AWS_Tests/src/S3_Spec.enso @@ -204,7 +204,7 @@ add_specs suite_builder = suite_builder.group "S3.list_objects" pending=api_pending group_builder-> group_builder.specify "should be able to list objects" <| objects = S3.list_objects bucket_name credentials=test_credentials - objects . should_contain object_name + objects . map .name . should_contain object_name group_builder.specify "should attach a warning if not a complete list" <| objects = S3.list_objects bucket_name max_count=1 credentials=test_credentials