Skip to content

Commit

Permalink
Custom API Support (#54)
Browse files Browse the repository at this point in the history
Adding Custom API class and field to DuneClient. There is a skipped test that does run when not ignored (but its specific to user name).

Sample usages was added to the README:

---------

Co-authored-by: Philipp Wassibauer <[email protected]>
  • Loading branch information
bh2smith and philippWassibauer authored Jun 12, 2024
1 parent 70c2986 commit 9c6fda9
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 2 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ client
// ]
```

## Custom API

```ts
const { DUNE_API_KEY } = process.env;

const client = new DuneClient(DUNE_API_KEY ?? "");
const results = await client.custom.getResults({
username: "your_username",
slug: "endpoint-slug"
// optional arguments: see `GetResultParams`
limit: 100,
});
```


Note also that the client has methods `executeQuery`, `getExecutionStatus`, `getExecutionResult` and `cancelExecution`

Check out this [Demo Project](https://github.com/bh2smith/demo-ts-dune-client)!
4 changes: 4 additions & 0 deletions src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ExecutionAPI } from "./execution";
import { POLL_FREQUENCY_SECONDS } from "../constants";
import { QueryAPI } from "./query";
import { TableAPI } from "./table";
import { CustomAPI } from "./custom";

/// Various states of query execution that are "terminal".
const TERMINAL_STATES = [
Expand All @@ -43,11 +44,14 @@ export class DuneClient {
query: QueryAPI;
/// Table Management Interface
table: TableAPI;
/// Custom Endpoint Interface
custom: CustomAPI;

constructor(apiKey: string) {
this.exec = new ExecutionAPI(apiKey);
this.query = new QueryAPI(apiKey);
this.table = new TableAPI(apiKey);
this.custom = new CustomAPI(apiKey);
}

/**
Expand Down
31 changes: 31 additions & 0 deletions src/api/custom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Router } from "./router";
import { CustomAPIParams, ResultsResponse } from "../types";

/**
* Custom API Interface:
* Create custom API endpoints from existing Dune queries.
* https://docs.dune.com/api-reference/custom/overview
*/
export class CustomAPI extends Router {
/**
* Custom Endpoints allow developers to create and manage API
* endpoints from Dune queries.
* By selecting a query and scheduling it to run at a specified
* frequency, developers can call a custom URL to consume data.
* This flexible alternative to Preset Endpoints provides greater
* customization without the complexities of SQL Endpoints.
*
* @param {CustomAPIParams} args - Parameters for the custom API request.
* @see {@link CustomAPIParams}
* @returns {Promise<ResultsResponse>} - The result of the API call.
* @see {@link ResultsResponse}
*/
async getResults(args: CustomAPIParams): Promise<ResultsResponse> {
const x = await this._get<ResultsResponse>(
`endpoints/${args.handle}/${args.slug}/results`,
args,
);

return x;
}
}
1 change: 1 addition & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./client";
export * from "./custom";
export * from "./execution";
export * from "./query";
export * from "./router";
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { DuneClient, QueryAPI, ExecutionAPI } from "./api";
export { DuneClient, QueryAPI, ExecutionAPI, CustomAPI, TableAPI } from "./api";
export * from "./types";
export { Paginator } from "./paginator";
17 changes: 17 additions & 0 deletions src/types/requestArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,23 @@ export interface GetResultParams extends BaseParams {
columns?: string[] | string;
}

/**
* Custom API parameters for creating and managing custom endpoints.
* Extends GetResultParams but omits 'query_parameters'.
*
* @extends {Omit<GetResultParams, "query_parameters">}
*/
export interface CustomAPIParams extends Omit<GetResultParams, "query_parameters"> {
/**
* The team or user handle owning the custom endpoint.
*/
handle: string;
/**
* Custom endpoint slug.
*/
slug: string;
}

export function validateAndBuildGetResultParams({
limit,
offset,
Expand Down
26 changes: 26 additions & 0 deletions tests/e2e/custom.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CustomAPI } from "../../src/";
import log from "loglevel";
import { BASIC_KEY } from "./util";
import { expect } from "chai";

log.setLevel("silent", true);

describe("Custom API", () => {
let client: CustomAPI;
const slug = "test-custom-api";

before(() => {
client = new CustomAPI(BASIC_KEY);
});

// Skip: This endpoint is very "user specific"
it.skip("retrieves data from custom endpoint", async () => {
// Note: for DuneClient class this would be `client.custom.getResults`
const results = await client.getResults({
handle: "bh2smith",
slug,
limit: 1,
});
expect(results.result!.rows.length).to.equal(1);
});
});
3 changes: 2 additions & 1 deletion tests/e2e/util.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from "chai";
import { DuneError } from "../../src";

const { BASIC_API_KEY, PLUS_API_KEY, DUNE_USER_NAME } = process.env;
const { BASIC_API_KEY, PLUS_API_KEY, DUNE_USER_NAME, CUSTOM_SLUG } = process.env;
if (BASIC_API_KEY === undefined) {
throw Error("Missing ENV var: BASIC_API_KEY");
}
Expand All @@ -11,6 +11,7 @@ if (PLUS_API_KEY === undefined) {
export const BASIC_KEY: string = BASIC_API_KEY!;
export const PLUS_KEY: string = PLUS_API_KEY!;
export const USER_NAME: string = DUNE_USER_NAME || "your_username";
export const CUSTOM_API_SLUG: string = CUSTOM_SLUG || "test-custom-api";

export const expectAsyncThrow = async (
promise: Promise<unknown>,
Expand Down

0 comments on commit 9c6fda9

Please sign in to comment.