ash-project / ash_postgres

The PostgreSQL data layer for Ash Framework
https://hexdocs.pm/ash_postgres
MIT License
139 stars 75 forks source link

protocol Jason.Encoder not implemented for dynamic ([], []) of type Ecto.Query.DynamicExpr #299

Closed Winawer closed 5 months ago

Winawer commented 5 months ago

Describe the bug

iex(68)> {:ok, flowchart} = Mailcoach.Document.export(document_id)
[... snip for PII ..., but the object is a complex nested map]
iex(71)> Jason.encode!(flowchart) |> Jason.decode!()
[ ... snip for PII, but it encodes and decodes okay with no error ...]
iex(73)> {:ok, letter} = Ash.get(Mailcoach.Assessments.LetterQuestion, letter_id)
iex(74)> Ash.Changeset.for_update(letter, :update, %{completed_flowchart: flowchart})
[ ... snip for PII, but I get `valid?: true` in the Changeset
iex(74)> Ash.Changeset.for_update(letter, :update, %{completed_flowchart: flowchart}) |> Ash.update!()
** (Ash.Error.Unknown) Unknown Error

* ** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for dynamic([], []) of type Ecto.Query.DynamicExpr (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...

It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:

    @derive Jason.Encoder
    defstruct ...

Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:

    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
    Protocol.derive(Jason.Encoder, NameOfTheStruct)

  (jason 1.4.1) lib/jason.ex:213: Jason.encode_to_iodata!/2
  (postgrex 0.18.0) lib/postgrex/type_module.ex:947: Postgrex.DefaultTypes.encode_params/3
  (postgrex 0.18.0) lib/postgrex/query.ex:75: DBConnection.Query.Postgrex.Query.encode/3
  (db_connection 2.6.0) lib/db_connection.ex:1416: DBConnection.maybe_encode/4
  (db_connection 2.6.0) lib/db_connection.ex:794: DBConnection.execute/4
  (ecto_sql 3.11.2) lib/ecto/adapters/postgres/connection.ex:133: Ecto.Adapters.Postgres.Connection.execute/4
  (ecto_sql 3.11.2) lib/ecto/adapters/sql.ex:996: Ecto.Adapters.SQL.execute!/5
  (ecto_sql 3.11.2) lib/ecto/adapters/sql.ex:952: Ecto.Adapters.SQL.execute/6
  (ecto 3.11.2) lib/ecto/repo/queryable.ex:232: Ecto.Repo.Queryable.execute/4
  (ash_postgres 2.0.2) lib/data_layer.ex:1289: AshPostgres.DataLayer.update_query/4
  (ash 3.0.2) lib/ash/actions/update/bulk.ex:438: Ash.Actions.Update.Bulk.do_atomic_update/5
  (ash 3.0.2) lib/ash/actions/update/bulk.ex:227: Ash.Actions.Update.Bulk.run/6
  (ash 3.0.2) lib/ash/actions/update/update.ex:145: Ash.Actions.Update.run/4
  (ash 3.0.2) lib/ash.ex:2450: Ash.update/3
  (ash 3.0.2) lib/ash.ex:2401: Ash.update!/3
  (stdlib 5.1.1) erl_eval.erl:746: :erl_eval.do_apply/7
  (elixir 1.15.6) src/elixir.erl:361: :elixir.eval_forms/4
  (elixir 1.15.6) lib/module/parallel_checker.ex:112: Module.ParallelChecker.verify/1
  (iex 1.15.6) lib/iex/evaluator.ex:331: IEx.Evaluator.eval_and_inspect/3
  (iex 1.15.6) lib/iex/evaluator.ex:305: IEx.Evaluator.eval_and_inspect_parsed/3
    (elixir 1.15.6) lib/process.ex:860: Process.info/2
    (ash 3.0.2) lib/ash/error/unknown.ex:3: Ash.Error.Unknown."exception (overridable 2)"/1
    (ash 3.0.2) /Users/winawer/Dropbox/Work/Dyad/Code/mailcoach/deps/splode/lib/splode.ex:211: Ash.Error.to_class/2
    (ash 3.0.2) lib/ash/error/error.ex:66: Ash.Error.to_error_class/2
    (ash 3.0.2) lib/ash/actions/update/bulk.ex:583: Ash.Actions.Update.Bulk.do_atomic_update/5
    (ash 3.0.2) lib/ash/actions/update/bulk.ex:227: Ash.Actions.Update.Bulk.run/6
    (ash 3.0.2) lib/ash/actions/update/update.ex:145: Ash.Actions.Update.run/4
    (ash 3.0.2) lib/ash.ex:2450: Ash.update/3
    (ash 3.0.2) lib/ash.ex:2401: Ash.update!/3

To Reproduce I don't have an MRE, it's a complex resource but all of my other updates work fine.

Expected behavior I expected the update to happen in the database and it fails instead.

Runtime

zachdaniel commented 5 months ago

Duplicate of #297