Skip to content

Commit 67726be

Browse files
committed
Add SDK subscribe function
1 parent 7081ebf commit 67726be

File tree

10 files changed

+171
-18
lines changed

10 files changed

+171
-18
lines changed

packages/sdk/src/account/account.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {atBlockId} from "../build/build-at-block-id.js"
33
import {getAccount} from "../build/build-get-account.js"
44
import {invariant} from "@onflow/util-invariant"
55
import {decodeResponse as decode} from "../decode/decode.js"
6-
import {send} from "../send/send.js"
6+
import {send} from "../transport/send"
77

88
/**
99
* @typedef {import("@onflow/typedefs").Account} Account
@@ -33,3 +33,5 @@ export function account(address, {height, id} = {}, opts) {
3333

3434
return send([getAccount(address)], opts).then(decode)
3535
}
36+
37+
subscribe("topic", {das})

packages/sdk/src/block/block.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {send} from "../send/send.js"
1+
import {send} from "../transport/send"
22
import {getBlock} from "../build/build-get-block"
33
import {atBlockHeight} from "../build/build-at-block-height.js"
44
import {atBlockId} from "../build/build-at-block-id.js"

packages/sdk/src/contract.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as root from "./sdk"
22
import * as decode from "./decode/decode.js"
33
import * as encode from "./encode/encode"
44
import * as interaction from "./interaction/interaction"
5-
import * as send from "./send/send.js"
5+
import * as send from "./transport/send"
66
import * as template from "@onflow/util-template"
77

88
const interfaceContract =

packages/sdk/src/node-version-info/node-version-info.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {send} from "../send/send.js"
1+
import {send} from "../transport/send"
22
import {decodeResponse as decode} from "../decode/decode.js"
33
import {getNodeVersionInfo} from "../build/build-get-node-version-info"
44
import {NodeVersionInfo} from "@onflow/typedefs"

packages/sdk/src/sdk.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as logger from "@onflow/util-logger"
22
// Base
33
export {build} from "./build/build.js"
44
export {resolve} from "./resolve/resolve.js"
5-
export {send} from "./send/send.js"
5+
export {send} from "./transport/send"
66
export {decode} from "./decode/sdk-decode.js"
77
export {
88
encodeTransactionPayload,
@@ -113,3 +113,5 @@ import * as TestUtils from "./test-utils"
113113
export {TestUtils}
114114

115115
export {VERSION} from "./VERSION"
116+
117+
export {subscribe} from "./transport/subscribe"
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import {Buffer} from "@onflow/rlp"
2-
import {send as defaultSend} from "@onflow/transport-http"
32
import {initInteraction, pipe} from "../interaction/interaction"
43
import * as ixModule from "../interaction/interaction"
5-
import {invariant} from "../build/build-invariant.js"
4+
import {invariant} from "../build/build-invariant"
65
import {response} from "../response/response"
76
import {config} from "@onflow/config"
8-
import {resolve as defaultResolve} from "../resolve/resolve.js"
7+
import {resolve as defaultResolve} from "../resolve/resolve"
8+
import {getTransport} from "./transport"
99

1010
/**
1111
* @description - Sends arbitrary scripts, transactions, and requests to Flow
12-
* @param {Array.<Function> | Function} args - An array of functions that take interaction and return interaction
13-
* @param {object} opts - Optional parameters
14-
* @returns {Promise<*>} - A promise that resolves to a response
12+
* @param args - An array of functions that take interaction and return interaction
13+
* @param opts - Optional parameters
14+
* @returns - A promise that resolves to a response
1515
*/
16-
export const send = async (args = [], opts = {}) => {
17-
const sendFn = await config.first(
18-
["sdk.transport", "sdk.send"],
19-
opts.send || defaultSend
20-
)
16+
export const send = async (
17+
args: Function | Function[] = [],
18+
opts: any = {}
19+
): Promise<any> => {
20+
const {send: sendFn} = await getTransport(opts)
2121

2222
invariant(
2323
sendFn,
@@ -31,10 +31,10 @@ export const send = async (args = [], opts = {}) => {
3131

3232
opts.node = opts.node || (await config().get("accessNode.api"))
3333

34-
if (Array.isArray(args)) args = pipe(initInteraction(), args)
34+
if (Array.isArray(args)) args = pipe(initInteraction(), args as any) as any
3535
return sendFn(
3636
await resolveFn(args),
37-
{config, response, ix: ixModule, Buffer},
37+
{config, response, ix: ixModule, Buffer} as any,
3838
opts
3939
)
4040
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {config} from "@onflow/config"
2+
import {subscribe} from "./subscribe"
3+
import {SdkTransport} from "@onflow/typedefs"
4+
import {getTransport} from "./transport"
5+
6+
jest.mock("./transport")
7+
8+
describe("subscribe", () => {
9+
let mockTransport: jest.Mocked<SdkTransport.Transport>
10+
11+
beforeEach(() => {
12+
jest.resetAllMocks()
13+
14+
mockTransport = {
15+
subscribe: jest.fn().mockReturnValue({
16+
unsubscribe: jest.fn(),
17+
}),
18+
send: jest.fn(),
19+
}
20+
jest.mocked(getTransport).mockResolvedValue(mockTransport)
21+
})
22+
23+
test("subscribes to a topic and returns a subscription", async () => {
24+
const topic = "topic" as SdkTransport.SubscriptionTopic
25+
const args = {foo: "bar"} as SdkTransport.SubscriptionArguments<any>
26+
const onData = jest.fn()
27+
const onError = jest.fn()
28+
29+
const sub = await config().overload(
30+
{
31+
"accessNode.api": "http://localhost:8080",
32+
},
33+
async () => {
34+
return await subscribe({topic, args, onData, onError})
35+
}
36+
)
37+
38+
expect(mockTransport.subscribe).toHaveBeenCalledTimes(1)
39+
expect(mockTransport.subscribe).toHaveBeenCalledWith(
40+
{topic, args, onData: expect.any(Function), onError},
41+
{node: "http://localhost:8080"}
42+
)
43+
44+
expect(sub).toStrictEqual({
45+
unsubscribe: expect.any(Function),
46+
})
47+
})
48+
})
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {config} from "@onflow/config"
2+
import {SdkTransport} from "@onflow/typedefs"
3+
import {getTransport} from "./transport"
4+
import {invariant} from "@onflow/util-invariant"
5+
6+
// TODO: OPTS FUNCTION
7+
export async function subscribe<T extends SdkTransport.SubscriptionTopic>(
8+
{
9+
topic,
10+
args,
11+
onData,
12+
onError,
13+
}: {
14+
topic: T
15+
args: SdkTransport.SubscriptionArguments<T>
16+
onData: (data: SdkTransport.SubscriptionData<T>) => void
17+
onError: (error: Error) => void
18+
},
19+
opts: {
20+
node?: string
21+
send?: SdkTransport.SendFn
22+
transport?: SdkTransport.Transport
23+
} = {}
24+
) {
25+
const transport = await getTransport(opts)
26+
const node = opts?.node || (await config().get("accessNode.api"))
27+
28+
invariant(
29+
!!node,
30+
`SDK Send Error: Either opts.node or "accessNode.api" in config must be defined.`
31+
)
32+
33+
// TODO: handle onError
34+
// Subscribe using the resolved transport
35+
return transport.subscribe(
36+
{
37+
topic,
38+
args,
39+
onData: data => {
40+
// TODO: decode function
41+
onData(decode(topic, data))
42+
},
43+
onError,
44+
},
45+
{
46+
node,
47+
...opts,
48+
}
49+
)
50+
}
51+
52+
export function decode<T extends SdkTransport.SubscriptionTopic>(
53+
topic: T,
54+
data: SdkTransport.SubscriptionData<T>
55+
): any {
56+
return data
57+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {config} from "@onflow/config"
2+
import {httpTransport as defaultTransport} from "@onflow/transport-http"
3+
import {SdkTransport} from "@onflow/typedefs"
4+
import {invariant} from "@onflow/util-invariant"
5+
6+
export async function getTransport(
7+
opts: {
8+
send?: SdkTransport.SendFn
9+
transport?: SdkTransport.Transport
10+
} = {}
11+
): Promise<SdkTransport.Transport> {
12+
invariant(
13+
opts.send == null || opts.transport == null,
14+
`SDK Transport Error: Cannot provide both "transport" and legacy "send" options.`
15+
)
16+
17+
const transportOrSend = await config().first<
18+
SdkTransport.Transport | SdkTransport.SendFn
19+
>(
20+
["sdk.transport", "sdk.send"],
21+
opts.transport || opts.send || defaultTransport
22+
)
23+
24+
if (isTransportObject(transportOrSend)) {
25+
// This is a transport object, return it directly
26+
return transportOrSend
27+
} else {
28+
// This is a legacy send function, wrap it in a transport object
29+
return {
30+
send: transportOrSend,
31+
subscribe: () => {
32+
throw new Error(
33+
"Subscribe not supported with legacy send function transport, please provide a transport object."
34+
)
35+
},
36+
}
37+
}
38+
}
39+
40+
function isTransportObject(
41+
transport: any
42+
): transport is SdkTransport.Transport {
43+
return transport.send !== undefined && transport.subscribe !== undefined
44+
}

0 commit comments

Comments
 (0)