Skip to content

Commit

Permalink
Tidy Data to not have the extension (was trying to publish type).
Browse files Browse the repository at this point in the history
Tests for JSON deserializing Date/Times.
  • Loading branch information
jdunkerley committed Sep 26, 2023
1 parent 86c52c8 commit ad1ccea
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 67 deletions.
58 changes: 1 addition & 57 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import project.Meta
import project.Network.HTTP.Header.Header
import project.Network.HTTP.HTTP
import project.Network.HTTP.HTTP_Method.HTTP_Method
import project.Network.HTTP.Request_Body.Request_Body
import project.Network.HTTP.Request.Request
import project.Network.HTTP.Request_Error
import project.Network.URI.URI
import project.Nothing.Nothing
import project.System.File.File
from project.Data.Boolean import Boolean, False, True
from project.Network.HTTP.Request_Body import all
from project.System.File_Format import Auto_Detect, File_Format

## ALIAS load, open
Expand Down Expand Up @@ -278,59 +278,3 @@ post (uri:(URI | Text)) (body:Request_Body=Request_Body.Empty) (method:HTTP_Meth
response = HTTP.post uri body method headers
if try_auto_parse_response.not then response.with_materialized_body else
response.decode if_unsupported=response.with_materialized_body

## ALIAS download, http get
GROUP Input
Fetches from the URI and returns the response, parsing the body if the
content-type is recognised. Returns an error if the status code does not
represent a successful response.

Arguments:
- method: The HTTP method to use. Defaults to `GET`.
- headers: The headers to send with the request. Defaults to an empty vector.
- try_auto_parse: If successful should the body be attempted to be parsed to
an Enso native object.
URI.fetch : HTTP_Method -> Vector (Header | Pair Text Text) -> Boolean -> Any
URI.fetch self method=HTTP_Method.Get headers=[] try_auto_parse=True =
Data.fetch self method headers try_auto_parse

## ALIAS upload, http post
GROUP Input
Writes the provided data to the provided URI. Returns the response,
parsing the body if the content-type is recognised. Returns an error if the
status code does not represent a successful response.

Arguments:
- uri: The URI to fetch.
- body: The data to write. See `Supported Body Types` below.
- method: The HTTP method to use. Must be one of `HTTP_Method.Post`,
`HTTP_Method.Put`, `HTTP_Method.Patch`. Defaults to `HTTP_Method.Post`.
- headers: The headers to send with the request. Defaults to an empty vector.
- try_auto_parse: If successful should the body be attempted to be parsed to
an Enso native object.

! Specifying Content Types

If the `body` parameter specifies an explicit content type, then it is an
error to also specify additional `Content-Type` headers in the `headers`
parameter. (It is not an error to specify multiple `Content-Type` values in
`headers`, however.)

! Supported Body Types

- Request_Body.Text: Sends a text string, with optional encoding and content
type.
- Request_Body.Json: Sends an Enso object, after converting it to JSON.
- Request_Body.Binary: Sends a file.
- Request_Body.Form_Data: Sends a form encoded as key/value pairs. The keys
must be `Text`, and the values must be `Text` or `File`.
- Request_Body.Empty: Sends an empty body.

Additionally, the following types are allowed as the `body` parameter:

- 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`.
URI.post : Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> Boolean -> Any
URI.post self (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=HTTP_Method.Post) (headers:(Vector (Header | Pair Text Text))=[]) (try_auto_parse_response:Boolean=True) =
Data.post self body method headers try_auto_parse_response
21 changes: 13 additions & 8 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,19 @@ make_enso js_object =
_ : Array ->
proxy = Array_Proxy.new js_object.length (i-> make_enso (js_object.at i))
Vector.from_polyglot_array proxy
_ : JS_Object ->
parsed = case js_object.get "type" of
"Date" -> Date.from js_object
"Date_Time" -> Date_Time.from js_object
"Time_Of_Day" -> Time_Of_Day.from js_object
_ -> js_object
if parsed.is_error then js_object else parsed
_ -> JS_Object.Value js_object
_ : JS_Object -> js_object
_ ->
wrapped = JS_Object.Value js_object

## Handle deserializing date and time types.
type_name = wrapped.get "type"
parsed = if type_name == "Date" then Date.from wrapped else
if type_name == "Date_Time" then Date_Time.from wrapped else
if type_name == "Time_Of_Day" then Time_Of_Day.from wrapped else
wrapped

if parsed.is_error then wrapped else parsed


## PRIVATE
Internal function to convert any JS_Objects into their native JS objects before passing to JS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,5 +475,8 @@ type Time_Of_Day
## PRIVATE
Time_Of_Day.from (that:JS_Object) =
## Must have hour and minute but second and nanosecond are optional
if that.get "type" == "Time_Of_Day" && ["hour", "minute"].all that.contains_key then Time_Of_Day.new (that.get "hour") (that.get "minute") (that.get "second" 0) nanosecond=(that.get "nanosecond" 0) else
Error.throw (Illegal_Argument.Error "Invalid JS_Object for Time_Of_Day.from.")
case that.get "type" == "Time_Of_Day" && ["hour", "minute"].all that.contains_key of
True ->
if that.contains_key "nanosecond" && (that.contains_key "second" . not) then Error.throw (Illegal_Argument.Error "Invalid JS_Object for Time_Of_Day.from.") else
Time_Of_Day.new (that.get "hour") (that.get "minute") (that.get "second" 0) nanosecond=(that.get "nanosecond" 0)
False -> Error.throw (Illegal_Argument.Error "Invalid JS_Object for Time_Of_Day.from.")
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import project.Any.Any
import project.Data
import project.Data.Pair.Pair
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Errors.Common.Syntax_Error
import project.Network.HTTP.Header.Header
import project.Network.HTTP.HTTP_Method.HTTP_Method
import project.Network.HTTP.Request_Body.Request_Body
import project.Network.URI.URI
from project.Data.Boolean import Boolean, False, True

## ALIAS parse_uri, uri from text
GROUP Conversions
Expand All @@ -16,3 +24,59 @@ import project.Network.URI.URI
example_parse = "http://example.com".to_uri
Text.to_uri : URI ! Syntax_Error
Text.to_uri self = URI.parse self

## ALIAS download, http get
GROUP Input
Fetches from the URI and returns the response, parsing the body if the
content-type is recognised. Returns an error if the status code does not
represent a successful response.

Arguments:
- method: The HTTP method to use. Defaults to `GET`.
- headers: The headers to send with the request. Defaults to an empty vector.
- try_auto_parse: If successful should the body be attempted to be parsed to
an Enso native object.
URI.fetch : HTTP_Method -> Vector (Header | Pair Text Text) -> Boolean -> Any
URI.fetch self method=HTTP_Method.Get headers=[] try_auto_parse=True =
Data.fetch self method headers try_auto_parse

## ALIAS upload, http post
GROUP Input
Writes the provided data to the provided URI. Returns the response,
parsing the body if the content-type is recognised. Returns an error if the
status code does not represent a successful response.

Arguments:
- uri: The URI to fetch.
- body: The data to write. See `Supported Body Types` below.
- method: The HTTP method to use. Must be one of `HTTP_Method.Post`,
`HTTP_Method.Put`, `HTTP_Method.Patch`. Defaults to `HTTP_Method.Post`.
- headers: The headers to send with the request. Defaults to an empty vector.
- try_auto_parse: If successful should the body be attempted to be parsed to
an Enso native object.

! Specifying Content Types

If the `body` parameter specifies an explicit content type, then it is an
error to also specify additional `Content-Type` headers in the `headers`
parameter. (It is not an error to specify multiple `Content-Type` values in
`headers`, however.)

! Supported Body Types

- Request_Body.Text: Sends a text string, with optional encoding and content
type.
- Request_Body.Json: Sends an Enso object, after converting it to JSON.
- Request_Body.Binary: Sends a file.
- Request_Body.Form_Data: Sends a form encoded as key/value pairs. The keys
must be `Text`, and the values must be `Text` or `File`.
- Request_Body.Empty: Sends an empty body.

Additionally, the following types are allowed as the `body` parameter:

- 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`.
URI.post : Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> Boolean -> Any
URI.post self (body:Request_Body=Request_Body.Empty) (method:HTTP_Method=HTTP_Method.Post) (headers:(Vector (Header | Pair Text Text))=[]) (try_auto_parse_response:Boolean=True) =
Data.post self body method headers try_auto_parse_response
14 changes: 14 additions & 0 deletions test/Tests/src/Data/Json_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ spec =
deep_err.should_fail_parsing_with "closing quote ] expected at position 34"
"123 4".should_fail_parsing_with "JSON cannot be fully parsed at position 4"

Test.specify "should be able to deserialize Dates" <|
'{"type": "Date", "constructor": "new", "year": 2018, "month": 7, "day": 3}'.should_parse_as (Date.new 2018 7 3)
'{"type": "Date", "year": 2025, "month": 5, "day": 12}'.should_parse_as (Date.new 2025 5 12)
'{"type": "Date", "month": 5, "day": 12}' . should_parse_as (JS_Object.from_pairs [["type", "Date"], ["month", 5], ["day", 12]])
'{"type": "Date", "year": 2019, "day": 12}' . should_parse_as (JS_Object.from_pairs [["type", "Date"], ["year", 2019], ["day", 12]])

Test.specify "should be able to deserialize Time_Of_Day" <|
'{"type": "Time_Of_Day", "constructor": "new", "hour": 22, "minute": 14, "second": 47}'.should_parse_as (Time_Of_Day.new 22 14 47)
'{"type": "Time_Of_Day", "hour": 12, "minute": 30}'.should_parse_as (Time_Of_Day.new 12 30 0)
'{"type": "Time_Of_Day", "hour": 18, "minute": 6, "second": 13, "nanosecond": 1234568}'.should_parse_as (Time_Of_Day.new 18 6 13 nanosecond=1234568)
'{"type": "Time_Of_Day", "minute": 14, "second": 47}' . should_parse_as (JS_Object.from_pairs [["type", "Time_Of_Day"], ["minute", 14], ["second", 47]])
'{"type": "Time_Of_Day", "hour": 14, "second": 47}' . should_parse_as (JS_Object.from_pairs [["type", "Time_Of_Day"], ["hour", 14], ["second", 47]])
'{"type": "Time_Of_Day", "hour": 18, "minute": 6, "nanosecond": 1234568}'.should_parse_as (JS_Object.from_pairs [["type", "Time_Of_Day"], ["hour", 18], ["minute", 6], ["nanosecond", 1234568]])

Test.group "JSON Serialization" <|
Test.specify "should print JSON structures to valid json" <|
"0".should_render_itself
Expand Down

0 comments on commit ad1ccea

Please sign in to comment.