re-graph icon indicating copy to clipboard operation
re-graph copied to clipboard

Support appsync subscription

Open lowecg opened this issue 1 year ago • 5 comments

As mentioned in issue #98, here are the changes necessary to support subscriptions with AppSync.

lowecg avatar Nov 11 '24 14:11 lowecg

Hi,

Thanks for this! I really appreciate the contribution. Do you know if the "subscriptions as data" is a common pattern, or is it something unique to Appsync? My inclination is to have something like an implementation "flavour" called Appsync, which would take care of any aspects unique to Appsync. The impl parts were intended more as a way of exposing the underlying transport libraries to empower users if they needed to tweak something at the transport level, but I see Appsync as more of a protocol thing.

What do you think?

Cheers

oliyh avatar Nov 12 '24 12:11 oliyh

Thank you for reviewing this, Oli.

I was trying to be agnostic in the naming of "subscription as data", but I'm not sure if it is a common pattern so the AppSync flavour makes sense. Noted on the usage of impl. I'll have a think about this and will put forward some changes this weekend.

lowecg avatar Nov 15 '24 06:11 lowecg

Hey Oli

I had a little think on this and have added a way define a custom payload function. I think it simplifies things considerably and can let you shape the payload anyway you choose.

Everything to support AppSync is now doable from the init:

(defn- encode [obj]
  (js/JSON.stringify (clj->js obj)))

(defn auth-session-callback [{:keys [token] :as data}]
  (let [graphql-endpoint-url (.-aws_appsync_graphqlEndpoint aws-exports)
        ;; https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html
        graphql-endpoint-ws-url (-> graphql-endpoint-url
                                    (string/replace #"^https:" "wss:")
                                    (string/replace #"\.appsync-api\." ".appsync-realtime-api."))

        appsync-host (extract-host graphql-endpoint-url)
        header-b64 (str->base64 (clj->json-str {"Host" appsync-host, "Authorization" token}))
        payload-b64 (str->base64 (clj->json-str {})) ;; documentation says an empty payload is required
        graphql-endpoint-ws-url-with-auth (str graphql-endpoint-ws-url "?header=" header-b64 "&payload=" payload-b64)]
    (rf/dispatch
      [:re-graph.core/init
       {:ws   {:url                  graphql-endpoint-ws-url-with-auth
               :supported-operations #{:subscribe}
               :create-payload       (fn create-payload-appsync [query]
                                       {:data       (encode query)
                                        :extensions {"authorization" {"Authorization" token
                                                                      "host"          appsync-host}}})}

        :http {:url                  graphql-endpoint-url
               :supported-operations #{:query :mutation}
               :impl                 {:with-credentials? false
                                      :headers           {"Authorization" token}}}}]))

  (rf/dispatch [:app.auth.events/current-session data]))

I look forward to your feedback.

Chris.

lowecg avatar Dec 03 '24 10:12 lowecg

Hey @oliyh any thoughts on this? I know you suggested flavours, but is this approach sufficient instead?

lowecg avatar Dec 07 '24 10:12 lowecg

Hi,

Very grateful for this. Busy weekend but I will try to look at this next week.

Cheers

oliyh avatar Dec 07 '24 20:12 oliyh