Skip to content

Commit

Permalink
feat: Allow using a custom node-fetch-compatible module for making re…
Browse files Browse the repository at this point in the history
…quests.
  • Loading branch information
adam-coster committed Sep 18, 2021
1 parent 473b167 commit f6034e9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ sandbox
sand\ box
debug.log
.env
temp/
temp/
package-lock.json
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
<h1 align="center">Bravo</h1>
<h2 align="center">The <i>(unofficial)</i> Favro SDK</h2>

> **⚠Warning⚠** *Bravo is in active development and may change substantially with any release. Check the changelog before updating!*
> **⚠Warning⚠** *Bravo features are likely under-tested as they come out, do your own testing before using in production, and use at your own risk!*
## Features

- 💧 Favro data is hydrated into feature-rich and explorable classes, with convenience functions galore
Expand All @@ -15,7 +11,11 @@
- 🔐 Credentials and Favro-specific request details handled automatically
- 💤 Lazy-loading of search results to minimize API calls

See the [Roadmap](./ROADMAP.md) for which Favro API features are implemented, planned, or backburnered.
See the [Roadmap](./ROADMAP.md) for which Favro API features are implemented, planned, or backburnered. If you can't find what you need, learn how to [contribute](CONTRIBUTING.md).

> **💡 Note:** Bravo is in active development and may change substantially with any release. Check the [changelog](./CHANGELOG.md) before updating!
> **💥 Warning:** Automations can do a lot of damage if something goes wrong, and we can't guarantee anything. Before using Bravo in your production Favro Organizations, test your code on a separate Favro Organization made just for testing. Have a plan for how to recover from data loss or other errors!
## Why?

Expand Down Expand Up @@ -54,29 +54,29 @@ const bravoClient = new BravoClient({
2. [Favro API types](#favro-api-types)
3. [Dependencies](#dependencies)
4. [Recipes](#recipes)
1. [Create a Bravo Client](#create-a-bravo-client)
2. [Create a New Card](#create-a-new-card)
3. [Search Existing Cards](#search-existing-cards)
4. [Update a Common Field on a Card](#update-a-common-field-on-a-card)
5. [Batch-Update a Card's Common Fields](#batch-update-a-cards-common-fields)
6. [Add a Card Attachment](#add-a-card-attachment)
7. [Ensure up-to-date data (clear caches)](#ensure-up-to-date-data-clear-caches)
1. [Create a Bravo Client](#create-a-bravo-client)
2. [Create a New Card](#create-a-new-card)
3. [Search Existing Cards](#search-existing-cards)
4. [Update a Common Field on a Card](#update-a-common-field-on-a-card)
5. [Batch-Update a Card's Common Fields](#batch-update-a-cards-common-fields)
6. [Add a Card Attachment](#add-a-card-attachment)
7. [Ensure up-to-date data (clear caches)](#ensure-up-to-date-data-clear-caches)
5. [The Favro Data Model](#the-favro-data-model)
1. [Collections](#collections)
2. [Widgets (a.k.a. "Boards")](#widgets-aka-boards)
3. [Columns (a.k.a. "Board Statuses")](#columns-aka-board-statuses)
4. [Cards](#cards)
5. [Built-In Card Fields](#built-in-card-fields)
6. [Custom Fields](#custom-fields)
1. [Collections](#collections)
2. [Widgets (a.k.a. "Boards")](#widgets-aka-boards)
3. [Columns (a.k.a. "Board Statuses")](#columns-aka-board-statuses)
4. [Cards](#cards)
5. [Built-In Card Fields](#built-in-card-fields)
6. [Custom Fields](#custom-fields)
6. [Tips, Tricks, and Limitations](#tips-tricks-and-limitations)
1. [API Rate Limits](#api-rate-limits)
2. [Searching](#searching)
3. [Member fields & "completion"](#member-fields--completion)
4. [Limited Markdown](#limited-markdown)
5. [Identifiers](#identifiers)
1. [Card Sequential IDs](#card-sequential-ids)
2. [Widget-specific `cardId`s](#widget-specific-cardids)
6. [Creating Boards](#creating-boards)
1. [API Rate Limits](#api-rate-limits)
2. [Searching](#searching)
3. [Member fields & "completion"](#member-fields--completion)
4. [Limited Markdown](#limited-markdown)
5. [Identifiers](#identifiers)
1. [Card Sequential IDs](#card-sequential-ids)
2. [Widget-specific `cardId`s](#widget-specific-cardids)
6. [Creating Boards](#creating-boards)

## Authentication

Expand Down
2 changes: 2 additions & 0 deletions src/lib/BravoClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { BravoTagDefinition } from './entities/BravoTag.js';
import type { BravoEntity } from './BravoEntity.js';
import type { FavroApi } from '$types/FavroApi.js';

export { FavroClientAuth as BravoClientAuth } from './clientLib/FavroClient.js';

type ConstructorFavroEntity<EntityData extends Record<string, any>> = new (
client: BravoClient,
data: EntityData,
Expand Down
33 changes: 26 additions & 7 deletions src/lib/clientLib/FavroClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ function cleanHeaders(rawHeaders: Record<string, any>) {
);
}

export interface FavroClientAuth {
token: string;
organizationId: string;
userEmail: string;
}

export class FavroClient {
protected _token!: string;
protected _organizationId!: string;
Expand All @@ -99,12 +105,14 @@ export class FavroClient {
protected _requestsMade = 0;
protected _limitResetsAt?: Date;
private _backendId?: string;
private _fetch = fetch;

constructor(options?: {
token?: string;
organizationId?: string;
userEmail?: string;
}) {
/**
* @param customFetch - Optional `node-fetch` replacement
* to be used for *all* requests. Must be
* a drop-in replacement!
*/
constructor(options?: FavroClientAuth, customFetch?: typeof fetch) {
for (const [optionsName, envName] of [
['token', 'FAVRO_TOKEN'],
['userEmail', 'FAVRO_USER_EMAIL'],
Expand All @@ -114,6 +122,9 @@ export class FavroClient {
assertBravoClaim(value, `A Favro ${optionsName} is required.`);
this[`_${optionsName}` as const] = value;
}
if (customFetch) {
this._fetch = customFetch;
}
}

get organizationId(): string | undefined {
Expand All @@ -136,9 +147,17 @@ export class FavroClient {
*
* @param url - Relative to the base URL https://favro.com/api/v1
*/
async request<EntityData extends Record<string, any> | null = null>(
async request<
EntityData extends Record<string, any> | null = null,
Fetcher extends typeof fetch = typeof fetch,
>(
url: string,
options?: OptionsFavroRequest,
/**
* Optionally override the default `node-fetch` client
* (must be a drop-in replacement!)
*/
customFetch?: Fetcher,
): Promise<FavroResponse<EntityData, this>> {
assertBravoClaim(
typeof this._requestsRemaining == 'undefined' ||
Expand Down Expand Up @@ -168,7 +187,7 @@ export class FavroClient {
this._requestsMade++;
const res = new FavroResponse<EntityData, this>(
this,
await fetch(url, {
await (customFetch || this._fetch)(url, {
method,
headers, // Force it to assume no undefineds
body,
Expand Down

0 comments on commit f6034e9

Please sign in to comment.