Fork me on GitHub
#fulcro
<
2020-11-14
>
Buidler15:11:15

I'm having an issue setting up server mutations and I can't figure out the cause. mutations.cljs

(defn delete-note*
  "Delete a Note and its CardList its Cards."
  [state-map note-id]
  (log/info "Deleting :note/id" note-id "and its CardList and Cards")
  (nstate/remove-entity state-map
                        [:note/id note-id]
                        #{:note/card-list :card-list/cards}))

(defmutation delete-note
  "Mutation: delete the note with `:note/id` from the note list with `:note-list`"
  [{note-id :note/id}]
  (action [{:keys [state]}] 
    (swap! state delete-note* note-id))
  (remote [env] true))
mutations.clj
(pc/defmutation delete-note [env {note-id :note/id}]
  {::pc/sym `delete-note}
  (swap! note-table dissoc note-id))
And my note-table looks like this:
(def note-table
  (atom
    {1 {:note/id 1 :note/text "foo from server"}
     2 {:note/id 2 :note/text "bar from server"}}))
When I have (remote [env] true) enabled, the :note/id in the application state after the mutation looks like this:
{2
 {:note/id 2, :note/text bar from server},
 1 nil}  ; why?!
If the (remote [env] true) line is commented out, it looks as I expect:
{2
 {:note/id 2, :note/text bar from server}}
The result from the server-side mutation looks correct:
#p[buidler.mutations/delete-note:15] (swap! note-table dissoc note-id) => {2 #:note {:id 2, :text "bar from server"}}

cjmurphy16:11:00

If you return an empty map {} from your server mutation does that fix the problem?

Buidler16:11:49

@U0D5RN0S1 nope, returning an empty map doesn't fix it. Same result.

cjmurphy16:11:29

So this would happen if your server mutation that did nothing and returned {} ? I want to rule out something else from the server affecting the client.

Buidler16:11:16

That's correct. I commented out the swap! form and just have it returning {} and it still happens.

cjmurphy16:11:55

And do the same for all your server mutations perhaps? Also what about loads from the client? Gonna have to rule everything else out.

Buidler16:11:33

It's my only server mutation at this point, so that should make things simpler 🙂

Buidler16:11:08

What/how should I check about client loads?

cjmurphy16:11:02

df/load search for that. That really does affect your client state, that's what it is supposed to do.

cjmurphy16:11:05

And then there is merge - two ways to do that.

Buidler16:11:56

From my ui the mutation is just called as such:

(div :.wide.column
          (dom/i :.ui.right.floated.icon.trash.alternate.outline
            {:onClick #(comp/transact! this [(m/delete-note {:note/id id})])}))

Buidler16:11:12

That's all there is to it, nothing else.

cjmurphy16:11:27

Is that :node/id being picked up on the server? I don't trust a mutation like that without and ~. Not that that's solving your problem. And I do know there is a way to get away with using and ~, just never used it.

cjmurphy16:11:55

I guess it is working b/c it calls the client part of the mutation, the action part.

Buidler18:11:19

@U0D5RN0S1 yeah, the parameter is coming through to the mutation. I can log it.

Buidler18:11:33

The note-table atom even gets updated to remove the requested note, when I have the code uncommented.

cjmurphy23:11:00

Have you tested with different versions of Fulcro? Submit a bug (especially) if everything works as expected with older versions. There is this open issue: https://github.com/fulcrologic/fulcro/issues/433. Different to yours but in the same area.

Buidler23:11:37

@U0D5RN0S1 Oh, interesting. Thanks for the link. I'm going to test that tomorrow and report if it works in another version.

Buidler14:11:41

I tried all the way down to Fulcro 3.2.17 and Pathom 2.2.0 - 2.2.30 and still could not make it work properly. I'm running out of ideas here.

nivekuil14:11:27

what is nstate/remove-entity? does that happen even if the server errors, like if the remote mutation doesn't exist? what happens if you have more notes in the state map, only the id you try to delete becomes nil?

nivekuil14:11:37

the server return value is probably a red herring. it shouldn't do anything to client state unless explicitly merged

cjmurphy15:11:10

Yes but from what's been said the server return is causing the problem. So there is some unintentional merging happening. 'shouldn't', yet is. But if your action did nothing you wouldn't get the problem. So the action prepares the state for a strange mutation return to cause the problem. You could put print statements in the fulcro source.

cjmurphy15:11:00

There's a function called integrate-mutation-return-value!. Surely the culprit! Maybe just have it do nothing on your own version of the fulcro library.

cjmurphy15:11:33

In com.fulcrologic.fulcro.mutations .

cjmurphy15:11:26

I wouldn't be doing anything with server errors while you investigate this problem.

nivekuil16:11:38

yeah, the swap! in that integrate mutation function only runs if there's wasn't a remote-error?, so if it has a different behavior on error then it's probably something going on there and I'd add logging to that function

Buidler17:11:45

I haven't seen any errors in the .shadow-cljs/server.stderr.log come up while doing this.

Buidler17:11:57

@U0D5RN0S1 I changed integrate-mutation-return-value! to:

(>defn integrate-mutation-return-value!
  "If there is a successful result from the remote mutation in `env` this function will merge it with app state
  (if there was a mutation join query), and will also rewrite any tempid remaps that were returned
  in all of the possible locations they might be in both app database and runtime application state (e.g. network queues).

  Typically used as part of the construction of a global default result handler for mutations.

  Returns env."
  [env]
  [::env => ::env]
  (print "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
  env
  #_
I can see the x get printed when I execute the mutation from the UI, but the result is still the same lol

cjmurphy20:11:19

I would then do the same with its caller, default-result-action! . That seems to then be everything that affects your app state.

Buidler21:11:01

I've narrowed it down to rewrite-tempids!.

Buidler21:11:38

Must be something in my code I guess. Hmm :thinking_face:

cjmurphy21:11:24

Yes. Always been a candidate. But I never mentioned it because it is supposed to only do anything when your return map has a tempids key, which yours does not. But there is some kind of cache there - I was just looking at the code.

cjmurphy21:11:20

And the cache is kept in app state. At least you are in the world of source code again, even if still some more trial and error...

Buidler21:11:56

I did have previously returned a tempids map in that mutation, but that code has been deleted for a while.

Buidler21:11:09

It was also pretty cool to see how you can hack around on library code using shadow-cljs. Haven't done that prior to now.

cjmurphy21:11:11

So a browser fresh (F5) would have solved the problem (by starting app state fresh)? And yes including the source code much easier these days. I've been using it more and more with RAD.

Buidler01:11:05

Nope, browser refresh doesn't solve it. Still having that same issue.