From a5aa22ec4cedee4774a0f2eb7443208aacb6f1b4 Mon Sep 17 00:00:00 2001 From: Adam Coster Date: Fri, 20 Aug 2021 10:46:32 -0500 Subject: [PATCH] feat: Rename card methods to refer to Card 'Instances' to be more explicit about what's happening. --- README.md | 6 ++++++ ROADMAP.md | 1 - src/lib/BravoClient.ts | 18 ++++++++++-------- src/lib/entities/BravoCard.ts | 13 ++++++------- src/lib/entities/BravoWidget.ts | 26 +++++++++++++++++++------- src/test/client.test.ts | 14 ++++++++------ 6 files changed, 49 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 903d9ac..99a8a5c 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,12 @@ Note that the card-search Favro API endpoint allows use of `sequentialId` as a q Note that all of these also appear to work for the user-friendly Card URLs (e.g. where the default URL ends with `?card=BSC-123` you could instead use `?card=123`). +#### Widget-specific `cardId`s + +Favro Cards are always return as an *instance* of that Card, each with its own `cardId`. + +Despite this ID being Widget-specific, and the existence of global idtentifiers for Cards, most of the Favro API card endpoints use this Widget-scoped `cardId`. Presumably this is to prevent them having to have distinct endpoints for managing global vs. Widget-scoped properties. + ### Creating Boards > 💡 Create boards manually via the app to get all the features you expect from Favro. diff --git a/ROADMAP.md b/ROADMAP.md index db7cb60..2100bb2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,7 +4,6 @@ - Revisit "Columns" implementation to more intuitively reflect what it's *for*. - Revisit "Custom Fields" implementation to make sure it reflects how Custom Fields are used. -- Revisit the class method and getter/setter names. Currently they are based directly on the field names from the Favro API, but many of those names are not easy to differentiate without referring to the docs (e.g. "cardId" vs "cardCommonId" vs "sequentialId"). Could keep the existing names but create more user-friendly aliases? ### Feature List diff --git a/src/lib/BravoClient.ts b/src/lib/BravoClient.ts index 7222b3a..076fb7a 100644 --- a/src/lib/BravoClient.ts +++ b/src/lib/BravoClient.ts @@ -517,7 +517,7 @@ export class BravoClient extends FavroClient { * * {@link https://favro.com/developer/#get-all-cards} */ - async listCards(options?: FavroApiGetCardsParams) { + async listCardInstances(options?: FavroApiGetCardsParams) { const res = (await this.requestWithReturnedEntities( `cards`, { @@ -533,16 +533,15 @@ export class BravoClient extends FavroClient { } /** - * Fetch a card with a known `sequentialId`. + * Fetch instances of a card with a known `sequentialId`. * The `sequentialId` is the numeric part of the incrementing * ID shown in the card's UI and shareable URL. * * @param cardSequentialId The card's number as shown in the UI * (can be just the number or include the prefix) */ - async findCardBySequentialId(cardSequentialId: number | string) { - const cards = await this.listCards({ cardSequentialId }); - return await cards.getFirstEntity(); + async findCardInstancesBySequentialId(cardSequentialId: number | string) { + return await this.listCardInstances({ cardSequentialId }); } /** @@ -581,7 +580,10 @@ export class BravoClient extends FavroClient { * * {@link https://favro.com/developer/#update-a-card} */ - async updateCardById(cardId: string, options: FavroApiParamsCardUpdate) { + async updateCardInstanceById( + cardId: string, + options: FavroApiParamsCardUpdate, + ) { const res = (await this.requestWithReturnedEntities( `cards/${cardId}`, { @@ -601,7 +603,7 @@ export class BravoClient extends FavroClient { * @param data If not provided, assumes `filename` exists and * attempts to use its content. */ - async addAttachmentToCard( + async addAttachmentToCardInstance( cardId: string, filename: string, data?: string | Buffer, @@ -625,7 +627,7 @@ export class BravoClient extends FavroClient { * * {@link https://favro.com/developer/#delete-a-card} */ - async deleteCard(cardId: string, everywhere = false) { + async deleteCardInstance(cardId: string, everywhere = false) { let url = `cards/${cardId}`; if (everywhere) { url += `?everywhere=true`; diff --git a/src/lib/entities/BravoCard.ts b/src/lib/entities/BravoCard.ts index b5c2f05..8dfd2ff 100644 --- a/src/lib/entities/BravoCard.ts +++ b/src/lib/entities/BravoCard.ts @@ -189,10 +189,6 @@ class BravoCardUpdateBuilder { } } -// TODO: Add a Favro-global-scope Card class as a base -// for specific Instances. (Still need to think through -// details of the data model...) - /** * A Card "Instance" represents the combination of a Card's * *global* data and its data associated with a specific Widget. @@ -329,7 +325,10 @@ export class BravoCardInstance extends BravoEntity { // the the user might want to start building a new update // before that returns. this._updateBuilder = new BravoCardUpdateBuilder(); - const updated = await this._client.updateCardById(this.cardId, data); + const updated = await this._client.updateCardInstanceById( + this.cardId, + data, + ); // Update this card! this._data = updated._data; return this; @@ -341,7 +340,7 @@ export class BravoCardInstance extends BravoEntity { * attempts to use its content. */ async attach(filename: string, data?: string | Buffer) { - const attachment = await this._client.addAttachmentToCard( + const attachment = await this._client.addAttachmentToCardInstance( this.cardId, filename, data, @@ -367,7 +366,7 @@ export class BravoCardInstance extends BravoEntity { * delete it everywhere else, too! */ async delete(everywhere = false) { - return this._client.deleteCard(this.cardId, everywhere); + return this._client.deleteCardInstance(this.cardId, everywhere); } equals(org: BravoCardInstance) { diff --git a/src/lib/entities/BravoWidget.ts b/src/lib/entities/BravoWidget.ts index d1e9511..ac20ba6 100644 --- a/src/lib/entities/BravoWidget.ts +++ b/src/lib/entities/BravoWidget.ts @@ -66,35 +66,47 @@ export class BravoWidget extends BravoEntity { /** * Get all cards on this Widget, with optional - * additional filter parameters. **Note:** makes + * additional filter parameters. There is only + * one instance of a card per Widget. + * + * **Note:** makes * an API request on *every call* (no caching), * so prefer re-use of the results to re-fetching, * and limit by `columnId` if possible. */ - async listCards(options?: FavroApiGetCardsBase) { - return await this._client.listCards({ + async listCardInstances(options?: FavroApiGetCardsBase) { + return await this._client.listCardInstances({ ...options, widgetCommonId: this.widgetCommonId, }); } - async findCard( + async findCardInstance( matchFunc: ArrayMatchFunction, options?: FavroApiGetCardsBase, ) { - const cards = await this.listCards(options); + const cards = await this.listCardInstances(options); return await cards.find(matchFunc); } - async findCardByName( + async findCardInstanceByName( name: string, options?: FavroApiGetCardsBase & { ignoreCase?: boolean }, ) { - return await this.findCard((card) => { + return await this.findCardInstance((card) => { return stringsMatch(card.name, name, options); }, options); } + async findCardInstanceBySequentialId(sequentialId: string) { + return ( + await this._client.listCardInstances({ + cardSequentialId: sequentialId, + widgetCommonId: this.widgetCommonId, + }) + ).getFirstEntity(); + } + async createCard(data: Omit) { return await this._client.createCard({ ...data, diff --git a/src/test/client.test.ts b/src/test/client.test.ts index ad2bba7..54daa71 100644 --- a/src/test/client.test.ts +++ b/src/test/client.test.ts @@ -230,16 +230,18 @@ describe('BravoClient', function () { it('can fetch a created card', async function () { this.timeout(8000); - const foundCard = await testWidget.findCardByName(testCardName); + const foundCard = await testWidget.findCardInstanceByName(testCardName); assertBravoTestClaim(foundCard); expect(foundCard!.equals(testCard)).to.be.true; // Fetch it again via sequentialId - const bySequentialId = await client.findCardBySequentialId( + const bySequentialId = await client.findCardInstancesBySequentialId( foundCard.sequentialId, ); - expect(bySequentialId!.equals(foundCard), 'can fetch by sequentialId').to - .be.true; + expect( + (await bySequentialId.getFirstEntity())!.equals(foundCard), + 'can fetch by sequentialId', + ).to.be.true; // Fetch it again via cardId const byCardId = await client.findCardInstanceById(foundCard.cardId); @@ -327,7 +329,7 @@ describe('BravoClient', function () { it('can add attachment to a card (and remove it)', async function () { const filename = 'hi.txt'; const attachedText = 'Hello World!'; - const attachment = await client.addAttachmentToCard( + const attachment = await client.addAttachmentToCardInstance( testCard.cardId, filename, attachedText, @@ -350,7 +352,7 @@ describe('BravoClient', function () { // Can't delete the last column, so we need to make another to delete! await testCard.delete(); expect( - await testWidget.findCardByName(testCardName), + await testWidget.findCardInstanceByName(testCardName), 'Should not find deleted card', ).to.be.undefined; });