Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
feat: use new dedicated websocket server (#521)
Browse files Browse the repository at this point in the history
* feat: use new dedicated websocket server

* feat: upgrade galoy client and remove legacy ws

* chore: websocket url

* chore: rm cast

* chore: update readme
  • Loading branch information
ntheile authored Jul 26, 2023
1 parent 808fa23 commit d02959f
Show file tree
Hide file tree
Showing 12 changed files with 577 additions and 78 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ In the project directory, create a file name `.env.local` and fill it with

```
NEXT_PUBLIC_GRAPHQL_HOSTNAME='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_HOSTNAME_INTERNAL='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_URI_INTERNAL='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_WEBSOCKET_URL='wss://ws.staging.galoy.io/graphql'
```

(or use your custom API URL), then run
Expand Down
5 changes: 5 additions & 0 deletions codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ documents:
- "lib/**/*.tsx"
- "pages/**/*.ts"
- "pages/**/*.tsx"
- "hooks/**/*.ts"
- "hooks/**/*.tsx"
generates:
lib/graphql/generated.ts:
schema: "lib/graphql/local-schema.gql"
Expand Down Expand Up @@ -50,3 +52,6 @@ generates:
Seconds: "number"
DisplayCurrency: "string"
SignedDisplayMajorAmount: "string"
CountryCode: "string"
Feedback: "string"
Minutes: "string"
29 changes: 22 additions & 7 deletions components/PaymentOutcome/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
import { useSubscription } from "@galoymoney/client"
import { useRouter } from "next/router"
import React, { useRef } from "react"
import Image from "react-bootstrap/Image"
import { ACTIONS, ACTION_TYPE } from "../../pages/_reducer"
import styles from "./payment-outcome.module.css"
import Receipt from "./receipt"
import { useReactToPrint } from "react-to-print"
import { gql } from "@apollo/client"
import { useLnInvoicePaymentStatusSubscription } from "../../lib/graphql/generated"
interface Props {
paymentRequest: string
paymentAmount: string | string[] | undefined
dispatch: React.Dispatch<ACTION_TYPE>
}

gql`
subscription lnInvoicePaymentStatus($input: LnInvoicePaymentStatusInput!) {
lnInvoicePaymentStatus(input: $input) {
__typename
errors {
message
__typename
}
status
}
}
`

function PaymentOutcome({ paymentRequest, paymentAmount, dispatch }: Props) {
const router = useRouter()
const { amount, sats, username, memo } = router.query
Expand All @@ -21,16 +35,17 @@ function PaymentOutcome({ paymentRequest, paymentAmount, dispatch }: Props) {
content: () => componentRef.current,
})

if (!paymentRequest) {
return null
}

const { loading, data, error, errorsMessage } = useSubscription.lnInvoicePaymentStatus({
const { loading, data, error } = useLnInvoicePaymentStatusSubscription({
variables: {
input: { paymentRequest },
},
skip: !paymentRequest,
})

if (!paymentRequest) {
return null
}

if (data !== undefined) {
if (error) console.error(error)
}
Expand Down Expand Up @@ -101,7 +116,7 @@ function PaymentOutcome({ paymentRequest, paymentAmount, dispatch }: Props) {
)
}

if (errors.length > 0 || errorsMessage) {
if (errors.length > 0 || error?.message) {
return (
<div className={styles.container}>
<div aria-labelledby="Payment unsuccessful">
Expand Down
5 changes: 4 additions & 1 deletion components/generate-invoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ function GenerateInvoice({

createInvoice({
variables: {
input: { recipientWalletId, amount: amt },
input: {
recipientWalletId,
amount: amt,
},
},
})
if (currency !== "SATS" || recipientWalletCurrency === "USD") {
Expand Down
7 changes: 3 additions & 4 deletions components/invoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import Tooltip from "react-bootstrap/Tooltip"
import Lottie from "react-lottie"
import { QRCode } from "react-qrcode-logo"

import { useSubscription } from "@galoymoney/client"

import animationData from "./success-animation.json"
import { useLnInvoicePaymentStatusSubscription } from "../lib/graphql/generated"

export default function Invoice({
paymentRequest,
Expand All @@ -19,7 +18,7 @@ export default function Invoice({
}) {
const [showCopied, setShowCopied] = useState(false)

const { loading, data, error, errorsMessage } = useSubscription.lnInvoicePaymentStatus({
const { loading, data, error } = useLnInvoicePaymentStatusSubscription({
variables: {
input: { paymentRequest },
},
Expand All @@ -40,7 +39,7 @@ export default function Invoice({

if (error) {
console.error(error)
return <div className="error">{errorsMessage}</div>
return <div className="error">{error.message}</div>
}

if (loading) {
Expand Down
55 changes: 50 additions & 5 deletions hooks/use-Create-Invoice.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,67 @@
import { useState } from "react"

import { useMutation } from "@galoymoney/client"
import { gql } from "@apollo/client"
import {
useLnInvoiceCreateOnBehalfOfRecipientsMutation,
useLnUsdInvoiceCreateOnBehalfOfRecipientMutation,
} from "../lib/graphql/generated"

interface Props {
recipientWalletCurrency: string | undefined
}

gql`
mutation lnUsdInvoiceCreateOnBehalfOfRecipient(
$input: LnUsdInvoiceCreateOnBehalfOfRecipientInput!
) {
lnUsdInvoiceCreateOnBehalfOfRecipient(input: $input) {
errors {
__typename
message
}
invoice {
__typename
paymentHash
paymentRequest
paymentSecret
satoshis
}
__typename
}
}
`

gql`
mutation lnInvoiceCreateOnBehalfOfRecipients(
$input: LnInvoiceCreateOnBehalfOfRecipientInput!
) {
lnInvoiceCreateOnBehalfOfRecipient(input: $input) {
errors {
__typename
message
}
invoice {
__typename
paymentHash
paymentRequest
paymentSecret
satoshis
}
__typename
}
}
`
const useCreateInvoice = ({ recipientWalletCurrency }: Props) => {
const [invoiceStatus, setInvoiceStatus] = useState<
"loading" | "new" | "need-update" | "expired"
>("loading")

const mutation =
recipientWalletCurrency === "USD"
? useMutation.lnUsdInvoiceCreateOnBehalfOfRecipient
: useMutation.lnInvoiceCreateOnBehalfOfRecipient
? useLnUsdInvoiceCreateOnBehalfOfRecipientMutation
: useLnInvoiceCreateOnBehalfOfRecipientsMutation

const [createInvoice, { loading, errorsMessage, error, data }] = mutation({
const [createInvoice, { loading, error, data }] = mutation({
onError: console.error,
onCompleted: () => setInvoiceStatus("new"),
})
Expand All @@ -26,7 +71,7 @@ const useCreateInvoice = ({ recipientWalletCurrency }: Props) => {
setInvoiceStatus,
invoiceStatus,
loading,
errorsMessage,
errorsMessage: error?.message,
error,
data,
}
Expand Down
3 changes: 2 additions & 1 deletion lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let GRAPHQL_HOSTNAME = process.env.NEXT_PUBLIC_GRAPHQL_HOSTNAME as string
const GRAPHQL_WEBSOCKET_URL = process.env.NEXT_PUBLIC_GRAPHQL_WEBSOCKET_URL ?? ""

// we need an internal dns to properly propagate the ip related headers to api
// if we use the api endpoints, nginx will rewrite the header to prevent spoofing
Expand All @@ -22,7 +23,7 @@ if (!GRAPHQL_HOSTNAME) {

const GRAPHQL_URI_INTERNAL = `http://${GRAPHQL_HOSTNAME_INTERNAL}/graphql`
const GRAPHQL_URI = `https://${GRAPHQL_HOSTNAME}/graphql`
const GRAPHQL_SUBSCRIPTION_URI = `wss://${GRAPHQL_HOSTNAME}/graphql`
const GRAPHQL_SUBSCRIPTION_URI = `${GRAPHQL_WEBSOCKET_URL}` // 'wss://ws.staging.galoy.io/graphql'

const NOSTR_PUBKEY = process.env.NOSTR_PUBKEY as string

Expand Down
63 changes: 55 additions & 8 deletions lib/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,83 @@ import {
InMemoryCache,
split,
HttpLink,
ApolloLink,
} from "@apollo/client"
import { WebSocketLink } from "@apollo/client/link/ws"
import { getMainDefinition } from "@apollo/client/utilities"
import { GraphQLWsLink } from "@apollo/client/link/subscriptions"
import { RetryLink } from "@apollo/client/link/retry"
import { onError } from "@apollo/client/link/error"

import { createClient } from "graphql-ws"

import { GRAPHQL_URI, GRAPHQL_SUBSCRIPTION_URI } from "./config"

const httpLink = new HttpLink({
uri: GRAPHQL_URI,
})

const wsLink = new WebSocketLink({
uri: GRAPHQL_SUBSCRIPTION_URI,
options: {
reconnect: true,
const wsLink = new GraphQLWsLink(
createClient({
url: GRAPHQL_SUBSCRIPTION_URI,
retryAttempts: 12,
connectionParams: {},
shouldRetry: (errOrCloseEvent) => {
console.warn({ errOrCloseEvent }, "entering shouldRetry function for websocket")
// TODO: understand how the backend is closing the connection
// for instance during a new version rollout or k8s upgrade
//
// in the meantime:
// returning true instead of the default 'Any non-`CloseEvent`'
// to force createClient to attempt a reconnection
return true
},
// Voluntary not using: webSocketImpl: WebSocket
// seems react native already have an implement of the websocket?
//
// TODO: implement keepAlive and reconnection?
// https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/client.ClientOptions.md#keepalive
}),
)

const errorLink = onError(({ graphQLErrors, networkError }) => {
// graphqlErrors should be managed locally
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) => {
if (message === "PersistedQueryNotFound") {
console.log(`[GraphQL info]: Message: ${message}, Path: ${path}}`, {
locations,
})
} else {
console.warn(`[GraphQL error]: Message: ${message}, Path: ${path}}`, {
locations,
})
}
})
// only network error are managed globally
if (networkError) {
console.log(`[Network error]: ${networkError}`)
}
})

const retryLink = new RetryLink({
attempts: {
max: 5,
},
})

const splitLink = split(
const link = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === "OperationDefinition" && definition.operation === "subscription"
)
},
wsLink,
httpLink,
ApolloLink.from([errorLink, retryLink, httpLink]),
)

const client = new ApolloClient({
link: splitLink,
link,
cache: new InMemoryCache(),
})

Expand Down
Loading

0 comments on commit d02959f

Please sign in to comment.