-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…d intial modularlization of the code. Basic types for the wire protocol. (#2)
- Loading branch information
1 parent
ba50619
commit df33094
Showing
10 changed files
with
901 additions
and
33 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module.exports = { | ||
env: { | ||
browser: true, | ||
es2021: true, | ||
node: true, | ||
}, | ||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], | ||
overrides: [], | ||
parser: "@typescript-eslint/parser", | ||
parserOptions: { | ||
project: "./tsconfig.json", | ||
tsConfigRootDir: __dirname, | ||
ecmaVersion: "latest", | ||
sourceType: "module", | ||
}, | ||
plugins: ["@typescript-eslint", "eslint-plugin-tsdoc"], | ||
rules: { | ||
"@typescript-eslint/no-explicit-any": ["off"], | ||
"tsdoc/syntax": "error", | ||
}, | ||
}; |
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 |
---|---|---|
@@ -1,4 +1,5 @@ | ||
#!/bin/sh | ||
. "$(dirname "$0")/_/husky.sh" | ||
|
||
yarn lint | ||
yarn pretty-quick --staged |
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,23 @@ | ||
import { Client } from "../../src/client"; | ||
import { endpoints } from "../../src/client-configuration"; | ||
|
||
describe("endpoints", () => { | ||
it("is extensible", async () => { | ||
endpoints["my-alternative-port"] = new URL("http://localhost:7443"); | ||
expect(endpoints).toEqual({ | ||
cloud: new URL("https://db.fauna.com"), | ||
local: new URL("http://localhost:8443"), | ||
"my-alternative-port": new URL("http://localhost:7443"), | ||
}); | ||
const client = new Client({ | ||
endpoint: endpoints["my-alternative-port"], | ||
maxConns: 5, | ||
secret: "secret", | ||
queryTimeoutMillis: 60, | ||
}); | ||
expect(client.client.defaults.baseURL).toEqual("http://localhost:7443/"); | ||
const result = await client.query<number>({ query: '"taco".length' }); | ||
expect(result.txn_time).not.toBeUndefined(); | ||
expect(result).toEqual({ data: 4, txn_time: result.txn_time }); | ||
}); | ||
}); |
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,28 @@ | ||
import { Client } from "../../src/client"; | ||
import { endpoints } from "../../src/client-configuration"; | ||
import { QueryError } from "../../src/wire-protocol"; | ||
import { env } from "process"; | ||
|
||
describe("query", () => { | ||
const client = new Client({ | ||
endpoint: env["endpoint"] ? new URL(env["endpoint"]) : endpoints.local, | ||
maxConns: 5, | ||
secret: env["secret"] || "secret", | ||
queryTimeoutMillis: 60, | ||
}); | ||
|
||
it("Can query an FQL-x endpoint", async () => { | ||
const result = await client.query<number>({ query: '"taco".length' }); | ||
expect(result.txn_time).not.toBeUndefined(); | ||
expect(result).toEqual({ data: 4, txn_time: result.txn_time }); | ||
}); | ||
|
||
it("Throws an error if the query is invalid", async () => { | ||
expect.assertions(1); | ||
try { | ||
await client.query<number>({ query: '"taco".length;' }); | ||
} catch (e) { | ||
expect(e).toEqual(new QueryError("Query failed.")); | ||
} | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* Configuration for a client. | ||
*/ | ||
export interface ClientConfiguration { | ||
/** | ||
* The {@link URL} of Fauna to call. See {@link endpoints} for some default options. | ||
*/ | ||
endpoint: URL; | ||
/** | ||
* The maximum number of connections to a make to Fauna. | ||
*/ | ||
maxConns: number; | ||
/** | ||
* A secret for your Fauna DB, used to authorize your queries. | ||
* @see https://docs.fauna.com/fauna/current/security/keys | ||
*/ | ||
secret: string; | ||
/** | ||
* The timeout of the query, in milliseconds. This controls the maximum amount of | ||
* time Fauna will execute your query before marking it failed. | ||
*/ | ||
queryTimeoutMillis: number; | ||
} | ||
|
||
/** | ||
* An extensible interface for a set of Fauna endpoints. | ||
* @remarks Leverage the `[key: string]: URL;` field to extend to other endpoints. | ||
*/ | ||
export interface Endpoints { | ||
/** Fauna's cloud endpoint. */ | ||
cloud: URL; | ||
/** | ||
* An endpoint for interacting with local instance of Fauna (e.g. one running in a local docker container). | ||
*/ | ||
local: URL; | ||
/** | ||
* Any other endpoint you want your client to support. For example, if you run all requests through a proxy | ||
* configure it here. Most clients will not need to leverage this ability. | ||
*/ | ||
[key: string]: URL; | ||
} | ||
|
||
/** | ||
* A extensible set of endpoints for calling Fauna. | ||
* @remarks Most clients will will not need to extend this set. | ||
* @example | ||
* ## To Extend | ||
* ```typescript | ||
* // add to the endpoints constant | ||
* endpoints.myProxyEndpoint = new URL("https://my.proxy.url"); | ||
* ``` | ||
*/ | ||
export const endpoints: Endpoints = { | ||
cloud: new URL("https://db.fauna.com"), | ||
local: new URL("http://localhost:8443"), | ||
}; |
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,79 @@ | ||
import axios, { type Axios } from "axios"; | ||
import Agent, { HttpsAgent } from "agentkeepalive"; | ||
import type { ClientConfiguration } from "./client-configuration"; | ||
import { | ||
QueryError, | ||
type QueryRequest, | ||
type QueryResponse, | ||
} from "./wire-protocol"; | ||
|
||
/** | ||
* Client for calling Fauna. | ||
*/ | ||
export class Client { | ||
/** The {@link ClientConfiguration} */ | ||
readonly clientConfiguration: ClientConfiguration; | ||
/** The underlying {@link Axios} client. */ | ||
readonly client: Axios; | ||
|
||
/** | ||
* Constructs a new {@link Client}. | ||
* @param clientConfiguration - the {@link ClientConfiguration} to apply. | ||
* @example | ||
* ```typescript | ||
* const myClient = new Client( | ||
* { | ||
* endpoint: endpoints.classic, | ||
* secret: "foo", | ||
* queryTimeoutMs: 60_000, | ||
* } | ||
* ); | ||
* ``` | ||
*/ | ||
constructor(clientConfiguration: ClientConfiguration) { | ||
this.clientConfiguration = clientConfiguration; | ||
// ensure the network timeout > ClientConfiguration.queryTimeoutMillis so we don't | ||
// terminate connections on active queries. | ||
const timeout = this.clientConfiguration.queryTimeoutMillis + 10_000; | ||
const agentSettings = { | ||
maxSockets: this.clientConfiguration.maxConns, | ||
maxFreeSockets: this.clientConfiguration.maxConns, | ||
timeout, | ||
// release socket for usage after 4s of inactivity. Must be less than Fauna's server | ||
// side idle timeout of 5 seconds. | ||
freeSocketTimeout: 4000, | ||
}; | ||
let httpAgents; | ||
if (this.clientConfiguration.endpoint.protocol === "http") { | ||
httpAgents = { httpAgent: new Agent(agentSettings) }; | ||
} else { | ||
httpAgents = { httpsAgent: new HttpsAgent(agentSettings) }; | ||
} | ||
this.client = axios.create({ | ||
baseURL: this.clientConfiguration.endpoint.toString(), | ||
timeout, | ||
...httpAgents, | ||
}); | ||
this.client.defaults.headers.common[ | ||
"Authorization" | ||
] = `Bearer ${this.clientConfiguration.secret}`; | ||
} | ||
|
||
/** | ||
* Queries Fauna. | ||
* @param queryRequest - the {@link QueryRequest} | ||
* @returns A {@link QueryResponse}. | ||
* @throws A {@link QueryError} if the request cannnot be completed. | ||
*/ | ||
async query<T = any>(queryRequest: QueryRequest): Promise<QueryResponse<T>> { | ||
try { | ||
const result = await this.client.post<QueryResponse<T>>( | ||
"/query/1", | ||
queryRequest | ||
); | ||
return result.data; | ||
} catch (e) { | ||
throw new QueryError("Query failed."); | ||
} | ||
} | ||
} |
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,32 @@ | ||
/** | ||
* A request to make to Fauna. | ||
*/ | ||
export interface QueryRequest { | ||
/** The query. */ | ||
query: string; | ||
} | ||
|
||
/** | ||
* A response to a query. | ||
* @remarks | ||
* The QueryResponse is type parameterized so that you can treat it as a | ||
* a certain type if you are using Typescript. | ||
*/ | ||
export interface QueryResponse<T> { | ||
/** | ||
* The result of the query. The data is any valid JSON value. | ||
* @remarks | ||
* data is type parameterized so that you can treat it as a | ||
* certain type if you are using typescript. | ||
*/ | ||
data: T; | ||
/** Stats on query performance and cost */ | ||
stats: any; | ||
/** The last transaction time of the query. An ISO-8601 date string. */ | ||
txn_time: string; | ||
} | ||
|
||
/** | ||
* An error representing a query failure. | ||
*/ | ||
export class QueryError extends Error {} |
Oops, something went wrong.