-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PKG -- [sdk] Create SDK subscribe function (#2024)
- Loading branch information
Showing
20 changed files
with
642 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import {SdkTransport} from "@onflow/typedefs" | ||
import {getTransport} from "./get-transport" | ||
import {httpTransport} from "@onflow/transport-http" | ||
import {config} from "@onflow/config" | ||
|
||
jest.mock("@onflow/transport-http", () => ({ | ||
httpTransport: { | ||
send: jest.fn(), | ||
subscribe: jest.fn(), | ||
} as jest.Mocked<SdkTransport.Transport>, | ||
})) | ||
|
||
describe("getTransport", () => { | ||
beforeEach(() => { | ||
jest.resetAllMocks() | ||
}) | ||
|
||
test("fallback to http transport", async () => { | ||
const transport = await getTransport() | ||
expect(transport).toBe(httpTransport) | ||
}) | ||
|
||
test("override with custom transport", async () => { | ||
const customTransport = { | ||
send: jest.fn(), | ||
subscribe: jest.fn(), | ||
} as jest.Mocked<SdkTransport.Transport> | ||
|
||
const transport = await getTransport({transport: customTransport}) | ||
expect(transport).toBe(customTransport) | ||
}) | ||
|
||
test("override with custom send function", async () => { | ||
const customSend = jest.fn() | ||
|
||
const transport = await getTransport({send: customSend}) | ||
expect(transport).toEqual({ | ||
send: customSend, | ||
subscribe: expect.any(Function), | ||
}) | ||
}) | ||
|
||
test("override with both custom transport and send function", async () => { | ||
await expect( | ||
getTransport({ | ||
send: jest.fn(), | ||
transport: { | ||
send: jest.fn(), | ||
subscribe: jest.fn(), | ||
}, | ||
}) | ||
).rejects.toThrow( | ||
/Cannot provide both "transport" and legacy "send" options/ | ||
) | ||
}) | ||
|
||
test("transport from global config - sdk.transport", async () => { | ||
const customTransport = { | ||
send: jest.fn(), | ||
subscribe: jest.fn(), | ||
} as jest.Mocked<SdkTransport.Transport> | ||
|
||
const tranpsort = await config().overload( | ||
{ | ||
"sdk.transport": customTransport, | ||
}, | ||
async () => { | ||
return await getTransport() | ||
} | ||
) | ||
|
||
expect(tranpsort).toBe(customTransport) | ||
}) | ||
|
||
test("send function from global config - sdk.transport", async () => { | ||
const customSend = jest.fn() | ||
|
||
const transport = await config().overload( | ||
{ | ||
"sdk.transport": customSend, | ||
}, | ||
async () => { | ||
return await getTransport() | ||
} | ||
) | ||
expect(transport).toEqual({ | ||
send: customSend, | ||
subscribe: expect.any(Function), | ||
}) | ||
}) | ||
|
||
test("send function from global config - sdk.send", async () => { | ||
const customSend = jest.fn() | ||
|
||
const transport = await config().overload( | ||
{ | ||
"sdk.send": customSend, | ||
}, | ||
async () => { | ||
return await getTransport() | ||
} | ||
) | ||
|
||
expect(transport).toEqual({ | ||
send: customSend, | ||
subscribe: expect.any(Function), | ||
}) | ||
}) | ||
|
||
test("custom transport has priority over global config", async () => { | ||
const customTransport = { | ||
send: jest.fn(), | ||
subscribe: jest.fn(), | ||
} as jest.Mocked<SdkTransport.Transport> | ||
|
||
const transport = await config().overload( | ||
{ | ||
"sdk.transport": httpTransport, | ||
}, | ||
async () => { | ||
return await getTransport({transport: customTransport}) | ||
} | ||
) | ||
|
||
expect(transport).toBe(customTransport) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import {config} from "@onflow/config" | ||
import {httpTransport as defaultTransport} from "@onflow/transport-http" | ||
import {SdkTransport} from "@onflow/typedefs" | ||
import {invariant} from "@onflow/util-invariant" | ||
|
||
/** | ||
* Get the SDK transport object, either from the provided override or from the global config. | ||
* @param overrides - Override default configuration with custom transport or send function. | ||
* @returns The SDK transport object. | ||
*/ | ||
export async function getTransport( | ||
override: { | ||
send?: SdkTransport.SendFn | ||
transport?: SdkTransport.Transport | ||
} = {} | ||
): Promise<SdkTransport.Transport> { | ||
invariant( | ||
override.send == null || override.transport == null, | ||
`SDK Transport Error: Cannot provide both "transport" and legacy "send" options.` | ||
) | ||
|
||
const transportOrSend = | ||
override.transport || | ||
override.send || | ||
(await config().first<SdkTransport.Transport | SdkTransport.SendFn>( | ||
["sdk.transport", "sdk.send"], | ||
defaultTransport | ||
)) | ||
|
||
// Backwards compatibility with legacy send function | ||
if (!isTransportObject(transportOrSend)) { | ||
return { | ||
send: transportOrSend, | ||
subscribe: () => { | ||
throw new Error( | ||
"Subscribe not supported with legacy send function transport, please provide a transport object." | ||
) | ||
}, | ||
} | ||
} | ||
|
||
return transportOrSend | ||
} | ||
|
||
function isTransportObject( | ||
transport: any | ||
): transport is SdkTransport.Transport { | ||
return ( | ||
transport.send !== undefined && | ||
transport.subscribe !== undefined && | ||
typeof transport.send === "function" && | ||
typeof transport.subscribe === "function" | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export {send} from "./send/send" | ||
export {subscribe} from "./subscribe/subscribe" | ||
export {rawSubscribe} from "./subscribe/raw-subscribe" |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import {Buffer} from "@onflow/rlp" | ||
import {initInteraction, pipe} from "../../interaction/interaction" | ||
import * as ixModule from "../../interaction/interaction" | ||
import {invariant} from "../../build/build-invariant" | ||
import {response} from "../../response/response" | ||
import {config} from "@onflow/config" | ||
import {resolve as defaultResolve} from "../../resolve/resolve" | ||
import {getTransport} from "../get-transport" | ||
|
||
/** | ||
* @description - Sends arbitrary scripts, transactions, and requests to Flow | ||
* @param args - An array of functions that take interaction and return interaction | ||
* @param opts - Optional parameters | ||
* @returns - A promise that resolves to a response | ||
*/ | ||
export const send = async ( | ||
args: Function | Function[] = [], | ||
opts: any = {} | ||
): Promise<any> => { | ||
const transport = await getTransport(opts) | ||
const sendFn = transport.send.bind(transport) | ||
|
||
invariant( | ||
sendFn, | ||
`Required value for sdk.transport is not defined in config. See: ${"https://github.com/onflow/fcl-js/blob/master/packages/sdk/CHANGELOG.md#0057-alpha1----2022-01-21"}` | ||
) | ||
|
||
const resolveFn = await config.first( | ||
["sdk.resolve"], | ||
opts.resolve || defaultResolve | ||
) | ||
|
||
opts.node = opts.node || (await config().get("accessNode.api")) | ||
|
||
if (Array.isArray(args)) args = pipe(initInteraction(), args as any) as any | ||
return sendFn( | ||
await resolveFn(args), | ||
{config, response, ix: ixModule, Buffer} as any, | ||
opts | ||
) | ||
} |
47 changes: 47 additions & 0 deletions
47
packages/sdk/src/transport/subscribe/raw-subscribe.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import {config} from "@onflow/config" | ||
import {rawSubscribe} from "./raw-subscribe" | ||
import {SdkTransport} from "@onflow/typedefs" | ||
import {getTransport} from "../get-transport" | ||
|
||
jest.mock("../get-transport") | ||
|
||
describe("subscribe", () => { | ||
let mockTransport: jest.Mocked<SdkTransport.Transport> | ||
let mockSub: jest.Mocked<SdkTransport.Subscription> = { | ||
unsubscribe: jest.fn(), | ||
} | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks() | ||
|
||
mockTransport = { | ||
subscribe: jest.fn().mockReturnValue(mockSub), | ||
send: jest.fn(), | ||
} | ||
jest.mocked(getTransport).mockResolvedValue(mockTransport) | ||
}) | ||
|
||
test("subscribes to a topic and returns subscription from transport", async () => { | ||
const topic = "topic" as SdkTransport.SubscriptionTopic | ||
const args = {foo: "bar"} as SdkTransport.SubscriptionArguments<any> | ||
const onData = jest.fn() | ||
const onError = jest.fn() | ||
|
||
const sub = await config().overload( | ||
{ | ||
"accessNode.api": "http://localhost:8080", | ||
}, | ||
async () => { | ||
return await rawSubscribe({topic, args, onData, onError}) | ||
} | ||
) | ||
|
||
expect(mockTransport.subscribe).toHaveBeenCalledTimes(1) | ||
expect(mockTransport.subscribe).toHaveBeenCalledWith( | ||
{topic, args, onData: onData, onError}, | ||
{node: "http://localhost:8080"} | ||
) | ||
|
||
expect(sub).toBe(mockSub) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import {config} from "@onflow/config" | ||
import {SdkTransport} from "@onflow/typedefs" | ||
import {getTransport} from "../get-transport" | ||
import {invariant} from "@onflow/util-invariant" | ||
import {SubscribeParams} from "./types" | ||
|
||
/** | ||
* Subscribe to a topic without decoding the data. | ||
* @param params - The parameters for the subscription. | ||
* @param opts - Additional options for the subscription. | ||
* @returns A promise that resolves once the subscription is active. | ||
*/ | ||
export async function rawSubscribe<T extends SdkTransport.SubscriptionTopic>( | ||
{topic, args, onData, onError}: SubscribeParams<T>, | ||
opts: { | ||
node?: string | ||
transport?: SdkTransport.Transport | ||
} = {} | ||
) { | ||
const transport = await getTransport(opts) | ||
const node = opts?.node || (await config().get("accessNode.api")) | ||
|
||
invariant( | ||
!!node, | ||
`SDK Send Error: Either opts.node or "accessNode.api" in config must be defined.` | ||
) | ||
|
||
// Subscribe using the resolved transport | ||
return transport.subscribe( | ||
{ | ||
topic, | ||
args, | ||
onData, | ||
onError, | ||
}, | ||
{ | ||
node, | ||
...opts, | ||
} | ||
) | ||
} |
Oops, something went wrong.