Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add fetchCandid() function to @dfinity/agent #630

Merged
merged 9 commits into from Sep 29, 2022
4 changes: 4 additions & 0 deletions docs/generated/changelog.html
Expand Up @@ -28,6 +28,10 @@ <h2>Version 0.13.3</h2>
This can be used to set proper Expiry times when a device has fallen out of sync with the
replica.
</li>
<li>
Adds a top-level <code>fetchCandid()</code> function which retrieves the Candid interface
for a given canister id.
</li>
</ul>

<h2>Version 0.13.2</h2>
Expand Down
27 changes: 27 additions & 0 deletions packages/agent/src/fetch_candid.test.ts
@@ -0,0 +1,27 @@
import { fetchCandid, HttpAgent } from '.';
import { IDL } from '@dfinity/candid';
import * as cbor from './cbor';

test('simulate fetching a Candid interface', async () => {
const mockFetch = jest.fn().mockImplementation((/*resource, init*/) => {
return Promise.resolve(
new Response(
cbor.encode({
status: 'replied',
reply: {
arg: IDL.encode([IDL.Text], ['service {}']),
},
}),
{
status: 200,
},
),
);
});

const agent = new HttpAgent({ fetch: mockFetch, host: 'http://localhost' });

const candid = await fetchCandid(agent, 'ryjl3-tyaaa-aaaaa-aaaba-cai');

expect(candid).toMatch(/service/);
});
31 changes: 31 additions & 0 deletions packages/agent/src/fetch_candid.ts
@@ -0,0 +1,31 @@
import { Actor, ActorSubclass, CanisterStatus, HttpAgent } from '.';
import { Principal } from '@dfinity/principal';
import { IDL } from '@dfinity/candid';

/**
* Retrieves the Candid interface for the specified canister.
*
* @param agent The agent to use for the request (usually an `HttpAgent`)
* @param canisterId A string corresponding to the canister ID
* @returns Candid source code
*/
export async function fetchCandid(agent: HttpAgent, canisterId: string): Promise<string> {
// Attempt to use canister metadata
const status = await CanisterStatus.request({
agent,
canisterId: Principal.fromText(canisterId),
paths: ['candid'],
});
const candid = status.get('candid') as string | undefined;
if (candid) {
return candid;
}

// Use `__get_candid_interface_tmp_hack` for canisters without Candid metadata
const tmpHackInterface: IDL.InterfaceFactory = ({ IDL }) =>
IDL.Service({
__get_candid_interface_tmp_hack: IDL.Func([], [IDL.Text], ['query']),
});
const actor: ActorSubclass = Actor.createActor(tmpHackInterface, { agent, canisterId });
return (await actor.__get_candid_interface_tmp_hack()) as string;
}
1 change: 1 addition & 0 deletions packages/agent/src/index.ts
Expand Up @@ -8,6 +8,7 @@ export * from './agent/http/transforms';
export * from './agent/http/types';
export * from './canisters/asset';
export * from './canisters/management';
export * from './fetch_candid';
export * from './request_id';
export * from './utils/bls';
export * from './utils/buffer';
Expand Down