Skip to content

Commit

Permalink
Writing to Data Links (#9750)
Browse files Browse the repository at this point in the history
- Closes #9292
- and also closes #9522
  • Loading branch information
radeusgd authored Apr 25, 2024
1 parent b150b09 commit 0c989c6
Show file tree
Hide file tree
Showing 37 changed files with 749 additions and 352 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@
- [Added `Table.running` method][9577]
- [Added `Excel_Workbook.read_many` allowing reading more than one sheet at a
time.][9759]
- [Added ability to write to Data Links.][9750]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -963,6 +964,7 @@
[9725]: https://github.com/enso-org/enso/pull/9725
[9759]: https://github.com/enso-org/enso/pull/9759
[9577]: https://github.com/enso-org/enso/pull/9577
[9750]: https://github.com/enso-org/enso/pull/9750

#### Enso Compiler

Expand Down
11 changes: 11 additions & 0 deletions distribution/lib/Standard/AWS/0.0.0-dev/src/AWS_Credential.enso
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,14 @@ type AWS_Credential
previous = Illegal_Argument.handle_java_exception <|
ClientBuilder.setDefaultCredentialOverride override.as_java
Panic.with_finalizer (ClientBuilder.setDefaultCredentialOverride previous) action

## PRIVATE
to_text self -> Text = case self of
AWS_Credential.Default -> "AWS_Credential.Default"
AWS_Credential.Profile profile -> "AWS_Credential.Profile " + profile
AWS_Credential.Key _ _ -> "AWS_Credential.Key"
AWS_Credential.With_Configuration base_credential region ->
base_credential.to_text + "(" + region.to_text + ")"

## PRIVATE
to_display_text self -> Text = self.to_text.to_display_text
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ private

from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
from Standard.Base.Enso_Cloud.Data_Link import parse_secure_value
from Standard.Base.Enso_Cloud.Data_Link_Helpers import parse_secure_value
from Standard.Base.Enso_Cloud.Public_Utils import get_required_field

import project.AWS_Credential.AWS_Credential
Expand Down
18 changes: 17 additions & 1 deletion distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_Data_Link.enso
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ private

from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.System.File.Generic.Writable_File.Writable_File
import Standard.Base.System.Input_Stream.Input_Stream
from Standard.Base.Enso_Cloud.Data_Link import Data_Link_With_Input_Stream, parse_format
import Standard.Base.System.Output_Stream.Output_Stream
from Standard.Base.Enso_Cloud.Data_Link_Capabilities import Data_Link_With_Input_Stream, Data_Link_With_Output_Stream, Writable_Data_Link
from Standard.Base.Enso_Cloud.Data_Link_Helpers import parse_format
from Standard.Base.Enso_Cloud.Public_Utils import get_optional_field, get_required_field

import project.AWS_Credential.AWS_Credential
Expand Down Expand Up @@ -38,5 +41,18 @@ type S3_Data_Link
with_input_stream self (open_options : Vector) (action : Input_Stream -> Any) -> Any =
self.as_file.with_input_stream open_options action

## PRIVATE
with_output_stream self (open_options : Vector) (action : Output_Stream -> Any) -> Any =
self.as_file.with_output_stream open_options action

## PRIVATE
as_writable_file self -> Writable_File = Writable_File.from self.as_file

## PRIVATE
Data_Link_With_Input_Stream.from (that:S3_Data_Link) = Data_Link_With_Input_Stream.Value that

## PRIVATE
Data_Link_With_Output_Stream.from (that:S3_Data_Link) = Data_Link_With_Output_Stream.Value that

## PRIVATE
Writable_Data_Link.from (that:S3_Data_Link) = Writable_Data_Link.Value that
27 changes: 15 additions & 12 deletions distribution/lib/Standard/AWS/0.0.0-dev/src/S3/S3_File.enso
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Enso_Cloud.Data_Link
import Standard.Base.Enso_Cloud.Data_Link.Data_Link
import Standard.Base.Enso_Cloud.Data_Link_Helpers
import Standard.Base.Errors.Common.Syntax_Error
import Standard.Base.Errors.File_Error.File_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
Expand Down Expand Up @@ -108,7 +109,7 @@ type S3_File
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 <|
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.write_data_link_as_stream self open_options action else
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
File_Access.ensure_only_allowed_options "with_output_stream" [File_Access.Write, File_Access.Create_New, File_Access.Truncate_Existing, File_Access.Create, Data_Link_Access.No_Follow] open_options <|
# The exists check is not atomic, but it is the best we can do with S3
Expand Down Expand Up @@ -140,7 +141,7 @@ type S3_File
with_input_stream : Vector File_Access -> (Input_Stream -> Any ! File_Error) -> Any ! S3_Error | Illegal_Argument
with_input_stream self (open_options : Vector) action = if self.is_directory then Error.throw (Illegal_Argument.Error "S3 folders cannot be opened as a stream." self.uri) else
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.read_data_link_as_stream self open_options action else
if open_as_data_link then Data_Link_Helpers.read_data_link_as_stream self open_options action else
File_Access.ensure_only_allowed_options "with_input_stream" [File_Access.Read, Data_Link_Access.No_Follow] open_options <|
response_body = translate_file_errors self <| S3.get_object self.s3_path.bucket self.s3_path.key self.credentials delimiter=S3_Path.delimiter
response_body.with_stream action
Expand All @@ -163,7 +164,7 @@ type S3_File
@format File_Format.default_widget
read : File_Format -> Problem_Behavior -> Any ! S3_Error
read self format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
if Data_Link.is_data_link self then Data_Link.read_data_link self format on_problems else
if Data_Link.is_data_link self then Data_Link_Helpers.read_data_link self format on_problems else
File_Format.handle_format_missing_arguments format <| case format of
Auto_Detect -> if self.is_directory then format.read self on_problems else
response = translate_file_errors self <| S3.get_object self.s3_path.bucket self.s3_path.key self.credentials delimiter=S3_Path.delimiter
Expand Down Expand Up @@ -236,17 +237,19 @@ type S3_File
- destination: the destination to move the file to.
- replace_existing: specifies if the operation should proceed if the
destination file already exists. Defaults to `False`.
copy_to : Writable_File -> Boolean -> Any ! File_Error
copy_to self (destination : Writable_File) (replace_existing : Boolean = 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 <|
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 <|
case destination.file of
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)
s3_destination : S3_File ->
if replace_existing.not && s3_destination.exists then Error.throw (File_Error.Already_Exists destination) else
destination_path = s3_destination.s3_path
translate_file_errors self <| S3.copy_object self.s3_path.bucket self.s3_path.key destination_path.bucket destination_path.key self.credentials . if_not_error <| s3_destination
_ -> generic_copy self destination.file replace_existing
translate_file_errors self <| S3.copy_object self.s3_path.bucket self.s3_path.key destination_path.bucket destination_path.key self.credentials
. if_not_error destination_writable.file_for_return
_ -> generic_copy self destination_writable replace_existing

## ICON data_output
Moves the file to the specified destination.
Expand All @@ -262,8 +265,8 @@ type S3_File
- destination: the destination to move the file to.
- replace_existing: specifies if the operation should proceed if the
destination file already exists. Defaults to `False`.
move_to : Writable_File -> Boolean -> Nothing ! File_Error
move_to self (destination : Writable_File) (replace_existing : Boolean = False) =
move_to : File_Like -> Boolean -> Nothing ! 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 <|
r = self.copy_to destination replace_existing=replace_existing
r.if_not_error <|
Expand Down Expand Up @@ -399,7 +402,7 @@ File_Format_Metadata.from (that : S3_File) = File_Format_Metadata.Value that.uri
File_Like.from (that : S3_File) = File_Like.Value that

## PRIVATE
Writable_File.from (that : S3_File) =
Writable_File.from (that : S3_File) = if Data_Link.is_data_link that then Data_Link_Helpers.interpret_data_link_as_writable_file that else
Writable_File.Value that S3_File_Write_Strategy.instance

## PRIVATE
Expand Down
7 changes: 4 additions & 3 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import project.Data.Pair.Pair
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Enso_Cloud.Data_Link
import project.Enso_Cloud.Data_Link.Data_Link
import project.Enso_Cloud.Data_Link_Helpers
import project.Error.Error
import project.Errors.File_Error.File_Error
import project.Errors.Illegal_Argument.Illegal_Argument
Expand Down Expand Up @@ -341,8 +342,8 @@ download (uri:(URI | Text)) file:Writable_File (method:HTTP_Method=HTTP_Method.G
case Data_Link.is_data_link response.body.metadata of
True ->
# If the resource was a data link, we follow it, download the target data and try to write it to a file.
data_link = Data_Link.interpret_json_as_data_link response.decode_as_json
Data_Link.save_data_link_to_file data_link file
data_link = Data_Link_Helpers.interpret_json_as_data_link response.decode_as_json
Data_Link_Helpers.save_data_link_to_file data_link file
False ->
response.write file

Expand Down
Loading

0 comments on commit 0c989c6

Please sign in to comment.