Skip to content

Commit

Permalink
Migrate to JSON (#2898)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonatanklosko authored Dec 20, 2024
1 parent b2152c2 commit 561b73a
Show file tree
Hide file tree
Showing 30 changed files with 128 additions and 113 deletions.
3 changes: 1 addition & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ config :logger, :console,
format: "$date $time $metadata[$level] $message\n",
metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
config :phoenix, :json_library, JSON

# Additional mime types
config :mime, :types, %{
Expand Down
4 changes: 2 additions & 2 deletions lib/livebook/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ defmodule Livebook.Application do
if encrypted_secrets do
case Livebook.Teams.decrypt(encrypted_secrets, secret_key) do
{:ok, json} ->
for {name, value} <- Jason.decode!(json) do
for {name, value} <- JSON.decode!(json) do
%Livebook.Secrets.Secret{name: name, value: value, hub_id: id}
end

Expand All @@ -358,7 +358,7 @@ defmodule Livebook.Application do
if encrypted_file_systems do
case Livebook.Teams.decrypt(encrypted_file_systems, secret_key) do
{:ok, json} ->
for %{"type" => type} = dumped_data <- Jason.decode!(json),
for %{"type" => type} = dumped_data <- JSON.decode!(json),
do: Livebook.FileSystems.load(type, dumped_data)

:error ->
Expand Down
2 changes: 1 addition & 1 deletion lib/livebook/hubs/dockerfile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ defmodule Livebook.Hubs.Dockerfile do
secret_key = Livebook.Teams.derive_key(hub.teams_key)

data
|> Jason.encode!()
|> JSON.encode!()
|> Livebook.Teams.encrypt(secret_key)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/livebook/hubs/team_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ defmodule Livebook.Hubs.TeamClient do

dumped_data =
decrypted_value
|> Jason.decode!()
|> JSON.decode!()
|> Map.put("external_id", file_system.id)

FileSystems.load(file_system.type, dumped_data)
Expand Down
44 changes: 33 additions & 11 deletions lib/livebook/live_markdown/export.ex
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ defmodule Livebook.LiveMarkdown.Export do
"kind" => cell.kind,
# Attributes may include arbitrary values, including sequences
# like "-->" that would mess our format, so we always encode them
"attrs" => cell.attrs |> ensure_order() |> Jason.encode!() |> Base.encode64(padding: false),
"attrs" => cell.attrs |> JSON.encode!(&encode_sorting/2) |> Base.encode64(padding: false),
"chunks" => cell.chunks && Enum.map(cell.chunks, &Tuple.to_list/1)
})
end
Expand Down Expand Up @@ -275,10 +275,17 @@ defmodule Livebook.LiveMarkdown.Export do
defp render_output(_output, _ctx), do: :ignored

defp encode_js_data(data) when is_binary(data), do: {:ok, data}
defp encode_js_data(data), do: data |> ensure_order() |> Jason.encode()

defp encode_js_data(data) do
try do
{:ok, JSON.encode!(data, &encode_sorting/2)}
rescue
_error -> :error
end
end

defp render_metadata(metadata) do
metadata_json = metadata |> ensure_order() |> Jason.encode!()
metadata_json = JSON.encode_to_iodata!(metadata, &encode_sorting/2)
["<!-- livebook:", metadata_json, " -->"]
end

Expand Down Expand Up @@ -349,7 +356,11 @@ defmodule Livebook.LiveMarkdown.Export do
case Livebook.Hubs.notebook_stamp(hub, notebook_source, metadata) do
{:ok, stamp} ->
offset = IO.iodata_length(notebook_source)
json = %{"offset" => offset, "stamp" => stamp} |> ensure_order() |> Jason.encode!()

json =
%{"offset" => offset, "stamp" => stamp}
|> JSON.encode_to_iodata!(&encode_sorting/2)

footer = ["\n", "<!-- livebook:", json, " -->", "\n"]
{footer, []}

Expand Down Expand Up @@ -391,16 +402,27 @@ defmodule Livebook.LiveMarkdown.Export do
end
end

defp ensure_order(%{} = map) when not is_struct(map) do
map
# Wraps JSON.protocol_encode/2 to encode maps as sorted objects
defp encode_sorting(term, encoder) when is_non_struct_map(term) do
term
|> Enum.sort()
|> Enum.map(fn {key, value} -> {key, ensure_order(value)} end)
|> Jason.OrderedObject.new()
|> encode_object(encoder)
end

defp ensure_order(list) when is_list(list) do
Enum.map(list, &ensure_order/1)
defp encode_sorting(term, encoder), do: JSON.protocol_encode(term, encoder)

defp encode_object([], _encoder), do: "{}"

defp encode_object(pairs, encoder) do
[[_comma | entry] | entries] =
Enum.map(pairs, fn {key, value} ->
[?,, encode_key(key, encoder), ?:, encoder.(value, encoder)]
end)

[?{, entry, entries, ?}]
end

defp ensure_order(term), do: term
defp encode_key(key, encoder) when is_binary(key) or is_atom(key), do: encoder.(key, encoder)
defp encode_key(key, _encoder) when is_integer(key), do: [?", Integer.to_string(key), ?"]
defp encode_key(key, _encoder) when is_float(key), do: [?", Float.to_string(key), ?"]
end
4 changes: 2 additions & 2 deletions lib/livebook/live_markdown/import.ex
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ defmodule Livebook.LiveMarkdown.Import do
end

defp livebook_json_to_element(json) do
data = Jason.decode!(json)
data = JSON.decode!(json)

case data do
%{"livebook_object" => "cell_input"} ->
Expand Down Expand Up @@ -251,7 +251,7 @@ defmodule Livebook.LiveMarkdown.Import do
attrs

encoded when is_binary(encoded) ->
encoded |> Base.decode64!(padding: false) |> Jason.decode!()
encoded |> Base.decode64!(padding: false) |> JSON.decode!()
end

chunks = if(chunks = data["chunks"], do: Enum.map(chunks, &List.to_tuple/1))
Expand Down
9 changes: 2 additions & 7 deletions lib/livebook/runtime/definitions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ defmodule Livebook.Runtime.Definitions do
dependency: %{dep: {:explorer, "~> 0.10.0"}, config: []}
}

jason = %{
name: "jason",
dependency: %{dep: {:jason, "~> 1.4"}, config: []}
}

stb_image = %{
name: "stb_image",
dependency: %{dep: {:stb_image, "~> 0.6.9"}, config: []}
Expand Down Expand Up @@ -309,11 +304,11 @@ defmodule Livebook.Runtime.Definitions do
data =
Kino.FS.file_path("{{NAME}}")
|> File.read!()
|> Jason.decode!()
|> JSON.decode!()
Kino.Tree.new(data)\
""",
packages: [kino, jason]
packages: [kino]
},
%{
type: :file_action,
Expand Down
8 changes: 4 additions & 4 deletions lib/livebook/runtime/dependencies.ex
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ defmodule Livebook.Runtime.Dependencies do
## Examples
iex> Livebook.Runtime.Dependencies.parse_term(~s|{:jason, "~> 1.3.0"}|)
{:ok, {:jason, "~> 1.3.0"}}
iex> Livebook.Runtime.Dependencies.parse_term(~s|{:req, "~> 0.5.0"}|)
{:ok, {:req, "~> 0.5.0"}}
iex> Livebook.Runtime.Dependencies.parse_term(~s|{:jason, "~> 1.3.0", runtime: false, meta: "data"}|)
{:ok, {:jason, "~> 1.3.0", runtime: false, meta: "data"}}
iex> Livebook.Runtime.Dependencies.parse_term(~s|{:req, "~> 0.5.0", runtime: false, meta: "data"}|)
{:ok, {:req, "~> 0.5.0", runtime: false, meta: "data"}}
iex> Livebook.Runtime.Dependencies.parse_term(~s|%{name: "Jake", numbers: [1, 2, 3.4]}|)
{:ok, %{name: "Jake", numbers: [1, 2, 3.4]}}
Expand Down
4 changes: 2 additions & 2 deletions lib/livebook/teams/requests.ex
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ defmodule Livebook.Teams.Requests do
type = FileSystems.type(file_system)
%{name: name} = FileSystem.external_metadata(file_system)
attrs = FileSystem.dump(file_system)
json = Jason.encode!(attrs)
json = JSON.encode!(attrs)

params = %{
name: name,
Expand All @@ -142,7 +142,7 @@ defmodule Livebook.Teams.Requests do
type = FileSystems.type(file_system)
%{name: name} = FileSystem.external_metadata(file_system)
attrs = FileSystem.dump(file_system)
json = Jason.encode!(attrs)
json = JSON.encode!(attrs)

params = %{
id: file_system.external_id,
Expand Down
2 changes: 1 addition & 1 deletion lib/livebook_web/channels/js_view_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ defmodule LivebookWeb.JSViewChannel do
rescue
error ->
case error do
%Protocol.UndefinedError{protocol: Jason.Encoder, value: value} ->
%Protocol.UndefinedError{protocol: JSON.Encoder, value: value} ->
{:error, "value #{inspect(value)} is not JSON-serializable, use another data type"}

error ->
Expand Down
4 changes: 2 additions & 2 deletions lib/livebook_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ defmodule LivebookWeb.CoreComponents do
def exec_js(socket, js, opts \\ []) do
opts = Keyword.validate!(opts, [:to])

Phoenix.LiveView.push_event(socket, "lb:exec_js", %{js: Jason.encode!(js.ops), to: opts[:to]})
Phoenix.LiveView.push_event(socket, "lb:exec_js", %{js: JSON.encode!(js.ops), to: opts[:to]})
end

@doc """
Expand All @@ -1080,6 +1080,6 @@ defmodule LivebookWeb.CoreComponents do
end

def hook_prop(value) do
Jason.encode!(value)
JSON.encode!(value)
end
end
4 changes: 2 additions & 2 deletions lib/livebook_web/helpers/codec_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ defmodule LivebookWeb.CodecHelpers do
"""
@spec encode_annotated_binary!(term(), binary()) :: binary()
def encode_annotated_binary!(meta, binary) do
meta = Jason.encode!(meta)
meta = JSON.encode!(meta)
meta_size = byte_size(meta)
<<meta_size::size(32), meta::binary, binary::binary>>
end
Expand All @@ -101,7 +101,7 @@ defmodule LivebookWeb.CodecHelpers do
@spec decode_annotated_binary!(binary()) :: {term(), binary()}
def decode_annotated_binary!(raw) do
<<meta_size::size(32), meta::binary-size(meta_size), binary::binary>> = raw
meta = Jason.decode!(meta)
meta = JSON.decode!(meta)
{meta, binary}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ defmodule LivebookWeb.SessionLive.SaveRuntimeConfigComponent do
def handle_event("load_config", %{"name" => name}, socket) do
secret = Enum.find(socket.assigns.hub_secrets, &(&1.name == name))

case Jason.decode(secret.value) do
case JSON.decode(secret.value) do
{:ok, config_defaults} ->
send_event(socket.assigns.target, {:load_config, config_defaults})
{:noreply, socket}
Expand Down Expand Up @@ -214,7 +214,7 @@ defmodule LivebookWeb.SessionLive.SaveRuntimeConfigComponent do
defp config_secret_changeset(socket, attrs) do
secret_prefix = socket.assigns.secret_prefix
hub = socket.assigns.hub
value = Jason.encode!(socket.assigns.save_config_payload)
value = JSON.encode!(socket.assigns.save_config_payload)
secret = %Livebook.Secrets.Secret{hub_id: hub.id, name: nil, value: value}

secret
Expand Down
4 changes: 2 additions & 2 deletions lib/livebook_web/plugs/user_plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ defmodule LivebookWeb.UserPlug do
else
encoded =
%{"name" => nil, "hex_color" => Livebook.EctoTypes.HexColor.random()}
|> Jason.encode!()
|> JSON.encode!()
|> Base.encode64()

# We disable HttpOnly, so that it can be accessed on the client
Expand All @@ -76,7 +76,7 @@ defmodule LivebookWeb.UserPlug do
defp mirror_user_data_in_session(conn) when conn.halted, do: conn

defp mirror_user_data_in_session(conn) do
user_data = conn.cookies["lb_user_data"] |> Base.decode64!() |> Jason.decode!()
user_data = conn.cookies["lb_user_data"] |> Base.decode64!() |> JSON.decode!()
put_session(conn, :user_data, user_data)
end

Expand Down
1 change: 0 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ defmodule Livebook.MixProject do
{:phoenix_live_dashboard, "~> 0.8.4"},
{:telemetry_metrics, "~> 1.0"},
{:telemetry_poller, "~> 1.0"},
{:jason, "~> 1.0"},
{:bandit, "~> 1.0"},
{:plug, "~> 1.16"},
{:plug_crypto, "~> 2.0"},
Expand Down
8 changes: 4 additions & 4 deletions test/livebook/intellisense_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,15 @@ defmodule Livebook.IntellisenseTest do
context = eval(do: nil)

assert %{
label: "Jason",
label: "Req",
kind: :module,
documentation: """
A blazing fast JSON parser and generator in pure Elixir.
The high-level API.
(module)\
""",
insert_text: "Jason"
} in Intellisense.get_completion_items("Jas", context, node())
insert_text: "Req"
} in Intellisense.get_completion_items("R", context, node())
end

test "Elixir no completion" do
Expand Down
2 changes: 1 addition & 1 deletion test/livebook/live_markdown/export_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,7 @@ defmodule Livebook.LiveMarkdown.ExportTest do

defp stamp_metadata(notebook, source) do
[_, json] = Regex.run(~r/<!-- livebook:(.*) -->\n$/, source)
%{"offset" => offset, "stamp" => stamp} = Jason.decode!(json)
%{"offset" => offset, "stamp" => stamp} = JSON.decode!(json)

hub = Livebook.Hubs.fetch_hub!(notebook.hub_id)
source = binary_slice(source, 0, offset)
Expand Down
Loading

0 comments on commit 561b73a

Please sign in to comment.