diff --git a/src/lib/BravoClient.ts b/src/lib/BravoClient.ts index 803367e..9f93c8a 100644 --- a/src/lib/BravoClient.ts +++ b/src/lib/BravoClient.ts @@ -16,24 +16,16 @@ import { BravoCollection } from './entities/BravoCollection'; import { BravoUser } from '$/lib/entities/BravoUser'; import { BravoOrganization } from '$entities/BravoOrganization'; import { BravoWidget } from '$entities/BravoWidget.js'; -import type { FavroApi } from '$favro'; import { BravoColumn } from './entities/BravoColumn.js'; import type { ArrayMatchFunction, RequiredBy } from '$/types/Utility.js'; -import type { - DataFavroCard, - DataFavroCardAttachment, - FavroApiGetCardsParams, - FavroApiParamsCardCreate, -} from '$/types/FavroCardTypes.js'; import { BravoCardInstance } from './entities/BravoCard.js'; import { BravoCustomFieldDefinition } from './entities/BravoCustomField.js'; -import type { DataFavroCustomFieldDefinition } from '$/types/FavroCustomFieldTypes.js'; -import type { FavroApiParamsCardUpdate } from '$/types/FavroCardUpdateTypes.js'; import type { FavroResponse } from './clientLib/FavroResponse.js'; import { readFileSync } from 'fs'; import { basename } from 'path'; import { BravoTagDefinition } from './entities/BravoTag.js'; import type { BravoEntity } from './BravoEntity.js'; +import type { FavroApi } from '$favro'; type ConstructorFavroEntity> = new ( client: BravoClient, @@ -179,12 +171,12 @@ export class BravoClient extends FavroClient { async createCollection( name: string, options?: { - publicSharing?: FavroApi.Collection.FieldTypes.Visibility; - background?: FavroApi.Collection.FieldTypes.ColorBackground; + publicSharing?: FavroApi.Collection.ModelFieldValue.Visibility; + background?: FavroApi.Collection.ModelFieldValue.ColorBackground; sharedToUsers?: { email?: string; userId?: string; - role: FavroApi.Collection.FieldTypes.Role; + role: FavroApi.Collection.ModelFieldValue.Role; }[]; }, ) { @@ -309,7 +301,7 @@ export class BravoClient extends FavroClient { 'widgets', { method: 'get', query: { collectionId } }, BravoWidget, - )) as BravoResponseEntities; + )) as BravoResponseEntities; this.cache.setWidgets(res, collectionId); } return this.cache.getWidgets(collectionId)!; @@ -451,7 +443,7 @@ export class BravoClient extends FavroClient { `columns`, { method: 'get', query: { widgetCommonId } }, BravoColumn, - )) as BravoResponseEntities; + )) as BravoResponseEntities; const columns = await res.getAllEntities(); this.cache.setColumns(widgetCommonId, columns); } @@ -496,7 +488,7 @@ export class BravoClient extends FavroClient { * * {@link https://favro.com/developer/#create-a-card} */ - async createCard(data: FavroApiParamsCardCreate) { + async createCard(data: FavroApi.Card.CreateBody) { const res = await this.requestWithReturnedEntities( `cards`, { @@ -520,7 +512,7 @@ export class BravoClient extends FavroClient { * * {@link https://favro.com/developer/#get-all-cards} */ - async listCardInstances(options?: FavroApiGetCardsParams) { + async listCardInstances(options?: FavroApi.Card.SearchQuery) { const res = (await this.requestWithReturnedEntities( `cards`, { @@ -528,10 +520,10 @@ export class BravoClient extends FavroClient { query: { descriptionFormat: 'markdown', ...options, - } as FavroApiGetCardsParams, + } as FavroApi.Card.SearchQuery, }, BravoCardInstance, - )) as BravoResponseEntities; + )) as BravoResponseEntities; return res; } @@ -565,10 +557,10 @@ export class BravoClient extends FavroClient { method: 'get', query: { descriptionFormat: 'markdown', - } as FavroApiGetCardsParams, + } as FavroApi.Card.SearchQuery, }, BravoCardInstance, - )) as BravoResponseEntities; + )) as BravoResponseEntities; return await res.getFirstEntity(); } @@ -586,7 +578,7 @@ export class BravoClient extends FavroClient { */ async updateCardInstanceByCardId( cardId: string, - options: FavroApiParamsCardUpdate, + options: FavroApi.Card.UpdateBody, ) { const res = (await this.requestWithReturnedEntities( `cards/${cardId}`, @@ -598,7 +590,7 @@ export class BravoClient extends FavroClient { body: options, }, BravoCardInstance, - )) as BravoResponseEntities; + )) as BravoResponseEntities; return await res.getFirstEntity(); } @@ -617,8 +609,9 @@ export class BravoClient extends FavroClient { method: 'post', body, query: { filename: basename(filename) }, - })) as FavroResponse; - const attachment = (await res.getParsedBody()) as DataFavroCardAttachment; + })) as FavroResponse; + const attachment = + (await res.getParsedBody()) as FavroApi.Card.ModelFieldValue.Attachment; assertBravoClaim(attachment?.fileURL, `Failed to add attachment`); return attachment; } @@ -654,14 +647,14 @@ export class BravoClient extends FavroClient { `tags`, { method: 'get' }, BravoTagDefinition, - )) as BravoResponseEntities; + )) as BravoResponseEntities; this.cache.tags = res; } return this.cache.tags!; } async createTagDefinition( - options: Partial>, + options: Partial>, ) { const res = (await this.requestWithReturnedEntities( `tags`, @@ -670,13 +663,13 @@ export class BravoClient extends FavroClient { body: options, }, BravoTagDefinition, - )) as BravoResponseEntities; + )) as BravoResponseEntities; return await res.getFirstEntity(); } async updateTagDefinition( options: RequiredBy< - Partial>, + Partial>, 'tagId' >, ) { @@ -687,7 +680,7 @@ export class BravoClient extends FavroClient { body: options, }, BravoTagDefinition, - )) as BravoResponseEntities; + )) as BravoResponseEntities; return await res.getFirstEntity(); } @@ -731,7 +724,7 @@ export class BravoClient extends FavroClient { { method: 'get' }, BravoCustomFieldDefinition, )) as BravoResponseEntities< - DataFavroCustomFieldDefinition, + FavroApi.CustomFieldDefinition.Model, BravoCustomFieldDefinition >; this.cache.customFields = res; diff --git a/src/lib/clientLib/BravoResponse.ts b/src/lib/clientLib/BravoResponse.ts index 7c43d48..461b2a7 100644 --- a/src/lib/clientLib/BravoResponse.ts +++ b/src/lib/clientLib/BravoResponse.ts @@ -1,24 +1,23 @@ import type { BravoClient } from '$lib/BravoClient'; import type { BravoEntity } from '$lib/BravoEntity'; import type { FavroResponse } from './FavroResponse'; -import type { DataFavroCustomFieldDefinition } from '$/types/FavroCustomFieldTypes.js'; import type { BravoWidget } from '$entities/BravoWidget.js'; import type { BravoCustomFieldDefinition } from '../entities/BravoCustomField.js'; import type { FavroApi } from '$favro'; import type { BravoTagDefinition } from '../entities/BravoTag.js'; export type BravoResponseWidgets = BravoResponseEntities< - FavroApi.Widget.Data, + FavroApi.Widget.Model, BravoWidget >; export type BravoResponseCustomFields = BravoResponseEntities< - DataFavroCustomFieldDefinition, + FavroApi.CustomFieldDefinition.Model, BravoCustomFieldDefinition >; export type BravoResponseTags = BravoResponseEntities< - FavroApi.Tag.Data, + FavroApi.Tag.Model, BravoTagDefinition >; diff --git a/src/lib/entities/BravoCard.ts b/src/lib/entities/BravoCard.ts index 2590805..28afe1a 100644 --- a/src/lib/entities/BravoCard.ts +++ b/src/lib/entities/BravoCard.ts @@ -1,8 +1,4 @@ -import type { - DataFavroCard, - DataFavroCustomFieldType, -} from '$/types/FavroCardTypes.js'; -import type { FavroApiParamsCardUpdate } from '$/types/FavroCardUpdateTypes.js'; +import type { FavroApi } from '$/index.js'; import type { ExtractKeysByValue } from '$/types/Utility.js'; import { BravoEntity } from '$lib/BravoEntity.js'; import { assertBravoClaim } from '../errors.js'; @@ -21,7 +17,7 @@ import { * A Card "Instance" represents the combination of a Card's * *global* data and its data associated with a specific Widget. */ -export class BravoCardInstance extends BravoEntity { +export class BravoCardInstance extends BravoEntity { get name() { return this._data.name; } @@ -377,7 +373,7 @@ export class BravoCardInstance extends BravoEntity { * only way to guarantee the desired field, since all Custom * Fields are global in the Favro API.* */ - async getCustomField( + async getCustomField( customFieldOrId: CustomFieldOrId, ) { const setFields = await this.getCustomFields(); @@ -419,7 +415,7 @@ export class BravoCardInstance extends BravoEntity { * an error will be thrown. * */ - async getCustomFieldByName( + async getCustomFieldByName( name: string | RegExp, type: FieldType, ) { @@ -513,7 +509,7 @@ export class BravoCardInstance extends BravoEntity { * If no argument is provided, uses * any changes made via this instance's `.updateBuilder` methods. */ - async update(data: FavroApiParamsCardUpdate | BravoCardUpdateBuilder) { + async update(data: FavroApi.Card.UpdateBody | BravoCardUpdateBuilder) { if (data instanceof BravoCardUpdateBuilder) { data = data.toJSON(); } diff --git a/src/lib/entities/BravoCardUpdateBuilder.ts b/src/lib/entities/BravoCardUpdateBuilder.ts index 8424106..02e3b7c 100644 --- a/src/lib/entities/BravoCardUpdateBuilder.ts +++ b/src/lib/entities/BravoCardUpdateBuilder.ts @@ -1,16 +1,5 @@ -import type { - DataFavroCardFavroAttachment, - DataFavroCustomFieldType, - DataFavroRating, -} from '$/types/FavroCardTypes.js'; -import type { - FavroApiParamsCardCustomField, - FavroApiParamsCardUpdate, - FavroApiParamsCardUpdateArrayField, - FavroApiParamsCardUpdateCustomField, -} from '$/types/FavroCardUpdateTypes.js'; -import type { DataFavroCustomFieldDefinition } from '$/types/FavroCustomFieldTypes.js'; -import type { RequiredBy } from '$/types/Utility.js'; +import type { FavroApi } from '$/index.js'; +import type { ExtractKeysByValue, RequiredBy } from '$/types/Utility.js'; import { assertBravoClaim } from '../errors.js'; import { addToUniqueArrayBy, @@ -29,12 +18,16 @@ import type { import type { BravoTagDefinition } from './BravoTag.js'; import type { BravoUser } from './BravoUser.js'; -export type CustomFieldOrId = +export type CustomFieldOrId = | string - | DataFavroCustomFieldDefinition + | FavroApi.CustomFieldDefinition.Model | BravoCustomFieldDefinition | BravoCustomField; +export type BravoCardFieldWithArrayValue = ExtractKeysByValue< + Required, + any[] +>; /** * A Card update can be pretty complex, and to save API * calls its best to do all desired updates in one go @@ -43,7 +36,7 @@ export type CustomFieldOrId = * approach. */ export class BravoCardUpdateBuilder { - private update: RequiredBy = { + private update: RequiredBy = { customFields: [], }; constructor() {} @@ -128,7 +121,9 @@ export class BravoCardUpdateBuilder { return this.addToUniqueArray('removeAttachments', fileUrls); } - addFavroAttachments(favroItems: DataFavroCardFavroAttachment[]) { + addFavroAttachments( + favroItems: FavroApi.Card.ModelFieldValue.FavroAttachment[], + ) { this.update.addFavroAttachments ||= []; ensureArrayExistsAndAddUniqueBy( this.update.addFavroAttachments, @@ -155,7 +150,7 @@ export class BravoCardUpdateBuilder { addToWidget( widgetCommonId: string, options?: Pick< - FavroApiParamsCardUpdate, + FavroApi.Card.UpdateBody, | 'columnId' | 'laneId' | 'dragMode' @@ -174,7 +169,7 @@ export class BravoCardUpdateBuilder { private setCustomFieldUniquely( customFieldOrId: CustomFieldOrId, - update: FavroApiParamsCardCustomField, + update: FavroApi.CustomFieldValue.UpdateBody, ) { const customFieldId = typeof customFieldOrId == 'string' @@ -211,7 +206,7 @@ export class BravoCardUpdateBuilder { customFieldId: CustomFieldOrId<'Single select'>, statusName: string | RegExp, fieldDefinition: - | DataFavroCustomFieldDefinition + | FavroApi.CustomFieldDefinition.Model<'Single select'> | BravoCustomFieldDefinition<'Single select'> | BravoCustomField<'Single select'>, ) { @@ -270,7 +265,7 @@ export class BravoCardUpdateBuilder { setCustomRating( customFieldId: CustomFieldOrId<'Rating'>, - rating: DataFavroRating, + rating: FavroApi.CustomFieldValue.ModelFieldValue.Rating, ) { return this.setCustomFieldUniquely(customFieldId, { total: rating }); } @@ -290,7 +285,7 @@ export class BravoCardUpdateBuilder { customFieldOrId: CustomFieldOrId<'Multiple select'>, optionNames: (string | RegExp)[], fieldDefinition: - | DataFavroCustomFieldDefinition + | FavroApi.CustomFieldDefinition.Model<'Multiple select'> | BravoCustomFieldDefinition<'Multiple select'> | BravoCustomField<'Multiple select'>, ) { @@ -321,7 +316,7 @@ export class BravoCardUpdateBuilder { : customFieldOrId.customFieldId; let update = this.update.customFields.find( (f) => f.customFieldId == customFieldId, - ) as FavroApiParamsCardUpdateCustomField<'Members'> | undefined; + ) as FavroApi.CustomFieldValue.UpdateBody<'Members'> | undefined; if (!update) { update = { customFieldId, @@ -414,10 +409,10 @@ export class BravoCardUpdateBuilder { * appear in another array, e.g. to prevent an "add" and "remove" * version of the same action from both containing the same value. */ - private addToUniqueArray( + private addToUniqueArray( updateField: Field, - values: FavroApiParamsCardUpdate[Field], - opposingField?: FavroApiParamsCardUpdateArrayField, + values: FavroApi.Card.UpdateBody[Field], + opposingField?: BravoCardFieldWithArrayValue, ) { this.update[updateField] ||= []; ensureArrayExistsAndAddUnique(this.update[updateField], values); diff --git a/src/lib/entities/BravoCollection.ts b/src/lib/entities/BravoCollection.ts index 0ce47ee..a9d8ec4 100644 --- a/src/lib/entities/BravoCollection.ts +++ b/src/lib/entities/BravoCollection.ts @@ -2,7 +2,7 @@ import { BravoEntity } from '../BravoEntity.js'; import type { BravoWidget } from './BravoWidget.js'; import type { FavroApi } from '$favro'; -export class BravoCollection extends BravoEntity { +export class BravoCollection extends BravoEntity { get name() { return this._data.name; } diff --git a/src/lib/entities/BravoColumn.ts b/src/lib/entities/BravoColumn.ts index 334ac37..ace7247 100644 --- a/src/lib/entities/BravoColumn.ts +++ b/src/lib/entities/BravoColumn.ts @@ -5,7 +5,7 @@ import type { FavroApi } from '$favro'; * A Favro Column is one of the available values for the default * "Status" field on a board (the same one used for KanBan view). */ -export class BravoColumn extends BravoEntity { +export class BravoColumn extends BravoEntity { /** The unique identifier for this column on its parent Widget.*/ get columnId() { return this._data.columnId; diff --git a/src/lib/entities/BravoCustomField.ts b/src/lib/entities/BravoCustomField.ts index f8c33ea..c161c47 100644 --- a/src/lib/entities/BravoCustomField.ts +++ b/src/lib/entities/BravoCustomField.ts @@ -1,25 +1,20 @@ -import type { - DataFavroCustomFieldsValues, - DataFavroCustomFieldType, - DataFavroRating, -} from '$/types/FavroCardTypes.js'; -import type { DataFavroCustomFieldDefinition } from '$/types/FavroCustomFieldTypes.js'; import { BravoEntity } from '$lib/BravoEntity.js'; import { assertBravoClaim, BravoError } from '../errors.js'; +import type { FavroApi } from '$favro'; -type DataFavroCustomFieldTypeWithChoices = - | 'Multiple select' - | 'Single select' - | 'Tags'; +type DataFavroCustomFieldTypeWithChoices = Extract< + FavroApi.CustomFieldType, + 'Multiple select' | 'Single select' | 'Tags' +>; type CustomFieldOption = - DataFavroCustomFieldDefinition['customFieldItems']; + FavroApi.CustomFieldDefinition.ModelFieldValue.SelectOption; type BravoHumanFriendlyFieldValues = { Number: number; Time: number; Text: string; - Rating: DataFavroRating; + Rating: FavroApi.CustomFieldValue.Models['Rating']['total']; Voting: { tally: number; voters: string[]; @@ -39,13 +34,13 @@ type BravoHumanFriendlyFieldValues = { }; export class BravoCustomFieldDefinition< - TypeName extends DataFavroCustomFieldType, -> extends BravoEntity> { + TypeName extends FavroApi.CustomFieldType, +> extends BravoEntity> { /** * Store a value from this Field Definition to allow operations * referencing the definition. */ - private _value?: DataFavroCustomFieldsValues[TypeName]; + private _value?: FavroApi.CustomFieldValue.Models[TypeName]; get name() { return this._data.name; @@ -84,7 +79,7 @@ export class BravoCustomFieldDefinition< } static isChoiceField( - fieldType: DataFavroCustomFieldType, + fieldType: FavroApi.CustomFieldType, ): fieldType is DataFavroCustomFieldTypeWithChoices { return ['Multiple select', 'Tags', 'Single select'].includes(fieldType); } @@ -113,10 +108,10 @@ export class BravoCustomFieldDefinition< * with helper methods for getting and setting the value on a card * in a user-friendly way. */ -export class BravoCustomField { +export class BravoCustomField { constructor( public readonly definition: BravoCustomFieldDefinition, - public readonly value?: DataFavroCustomFieldsValues[TypeName], + public readonly value?: FavroApi.CustomFieldValue.Models[TypeName], ) {} /** The type of this Custom Field */ @@ -198,34 +193,39 @@ export class BravoCustomField { switch (type) { case 'Number': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Number']).total; + return (value as FavroApi.CustomFieldValue.Models['Number']).total; case 'Time': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Time']).total; + return (value as FavroApi.CustomFieldValue.Models['Time']).total; case 'Text': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Text']).value; + return (value as FavroApi.CustomFieldValue.Models['Text']).value; case 'Rating': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Rating']).total; + return (value as FavroApi.CustomFieldValue.Models['Rating']).total; case 'Voting': - // @ts-expect-error ts(2322) + // @ts-expect-error return { - tally: (value as DataFavroCustomFieldsValues['Voting']).value.length, - voters: [...(value as DataFavroCustomFieldsValues['Voting']).value], + tally: (value as FavroApi.CustomFieldValue.Models['Voting']).value + .length, + voters: [ + ...(value as FavroApi.CustomFieldValue.Models['Voting']).value, + ], }; case 'Checkbox': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Checkbox']).value; + return (value as FavroApi.CustomFieldValue.Models['Checkbox']).value; case 'Date': // @ts-expect-error - return new Date((value as DataFavroCustomFieldsValues['Date']).value); + return new Date( + (value as FavroApi.CustomFieldValue.Models['Date']).value, + ); case 'Timeline': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Timeline']).timeline; + return (value as FavroApi.CustomFieldValue.Models['Timeline']).timeline; case 'Link': // @ts-expect-error - return (value as DataFavroCustomFieldsValues['Link']).link; + return (value as FavroApi.CustomFieldValue.Models['Link']).link; case 'Members': // @ts-expect-error return this.assignedTo; diff --git a/src/lib/entities/BravoOrganization.ts b/src/lib/entities/BravoOrganization.ts index 4a76f01..019ebbc 100644 --- a/src/lib/entities/BravoOrganization.ts +++ b/src/lib/entities/BravoOrganization.ts @@ -2,7 +2,7 @@ import type { FavroApi } from '$types/FavroApi'; import { BravoEntity } from '$lib/BravoEntity.js'; /** Hydrated Favro Organization. */ -export class BravoOrganization extends BravoEntity { +export class BravoOrganization extends BravoEntity { get name() { return this._data.name; } diff --git a/src/lib/entities/BravoTag.ts b/src/lib/entities/BravoTag.ts index 961c4ac..9c09729 100644 --- a/src/lib/entities/BravoTag.ts +++ b/src/lib/entities/BravoTag.ts @@ -2,7 +2,7 @@ import { BravoEntity } from '$lib/BravoEntity.js'; import type { FavroApi } from '$favro'; /** Hydrated Favro Organization. */ -export class BravoTagDefinition extends BravoEntity { +export class BravoTagDefinition extends BravoEntity { get name() { return this._data.name; } diff --git a/src/lib/entities/BravoUser.ts b/src/lib/entities/BravoUser.ts index 633e7cc..b8aae0a 100644 --- a/src/lib/entities/BravoUser.ts +++ b/src/lib/entities/BravoUser.ts @@ -22,7 +22,7 @@ class BravoUserBase< * A hydrated User entity, as returned from the Users API * endpoint. */ -export class BravoUser extends BravoUserBase { +export class BravoUser extends BravoUserBase { get name() { return this._data.name; } diff --git a/src/lib/entities/BravoWidget.ts b/src/lib/entities/BravoWidget.ts index ded23b3..6f68747 100644 --- a/src/lib/entities/BravoWidget.ts +++ b/src/lib/entities/BravoWidget.ts @@ -3,13 +3,14 @@ import { stringsMatch } from '$lib/utility.js'; import type { FavroApi } from '$favro'; import type { BravoColumn } from './BravoColumn.js'; import type { ArrayMatchFunction } from '$/types/Utility.js'; -import type { - FavroApiGetCardsBase, - FavroApiParamsCardCreate, -} from '$/types/FavroCardTypes.js'; import type { BravoCardInstance } from './BravoCard.js'; -export class BravoWidget extends BravoEntity { +export type BravoCardSearchQueryBase = Exclude< + FavroApi.Card.SearchQuery, + 'cardCommonId' | 'widgetCommonId' | 'collectionId' | 'cardSequentialId' +>; + +export class BravoWidget extends BravoEntity { get widgetCommonId() { return this._data.widgetCommonId; } @@ -72,7 +73,7 @@ export class BravoWidget extends BravoEntity { * so prefer re-use of the results to re-fetching, * and limit by `columnId` if possible. */ - async listCardInstances(options?: FavroApiGetCardsBase) { + async listCardInstances(options?: BravoCardSearchQueryBase) { return await this._client.listCardInstances({ ...options, widgetCommonId: this.widgetCommonId, @@ -81,7 +82,7 @@ export class BravoWidget extends BravoEntity { async findCardInstance( matchFunc: ArrayMatchFunction, - options?: FavroApiGetCardsBase, + options?: BravoCardSearchQueryBase, ) { const cards = await this.listCardInstances(options); return await cards.find(matchFunc); @@ -89,7 +90,7 @@ export class BravoWidget extends BravoEntity { async findCardInstanceByName( name: string, - options?: FavroApiGetCardsBase & { ignoreCase?: boolean }, + options?: BravoCardSearchQueryBase & { ignoreCase?: boolean }, ) { return await this.findCardInstance((card) => { return stringsMatch(card.name, name, options); @@ -105,7 +106,7 @@ export class BravoWidget extends BravoEntity { ).getFirstEntity(); } - async createCard(data: Omit) { + async createCard(data: Omit) { return await this._client.createCard({ ...data, widgetCommonId: this.widgetCommonId, diff --git a/src/test/client.test.ts b/src/test/client.test.ts index 42e86f9..9e10bb9 100644 --- a/src/test/client.test.ts +++ b/src/test/client.test.ts @@ -42,10 +42,10 @@ import { stringOrObjectToString, stringsOrObjectsToStrings, } from '$/lib/utility.js'; -import type { DataFavroCustomFieldType } from '$/types/FavroCardTypes.js'; import { assertBravoClaim } from '$/lib/errors.js'; import type { BravoUser } from '$/lib/entities/BravoUser.js'; import type { BravoTagDefinition } from '$/lib/entities/BravoTag.js'; +import type { FavroApi } from '$favro'; /** * @remarks A root .env file must be populated with the required @@ -102,7 +102,7 @@ function assertBravoTestClaim( * * We don't care which custom field we get, just that it is of the right type. */ -async function getCustomFieldByType( +async function getCustomFieldByType( client: BravoClient, card: BravoCardInstance, type: FieldType, @@ -278,7 +278,7 @@ describe('BravoClient', function () { }); }); - xdescribe('Collections', function () { + describe('Collections', function () { it('can create a collection', async function () { testCollection = await client.createCollection(testCollectionName); assertBravoTestClaim(testCollection, 'Collection not created'); diff --git a/src/types/FavroApi.ts b/src/types/FavroApi.ts index 21019db..e2a4b58 100644 --- a/src/types/FavroApi.ts +++ b/src/types/FavroApi.ts @@ -3,6 +3,30 @@ * See {@link https://favro.com/developer} */ export namespace FavroApi { + /** + * Custom fields come in a bunch of different high-level types, + * with a lot of overlap in data models but some differences. + * + * @remarks + * These type values are seen in many API contexts; aliases + * are added to those namepspaces to this type. + * + */ + export type CustomFieldType = + | 'Checkbox' + | 'Date' + | 'Link' + | 'Members' + | 'Multiple select' + | 'Number' + | 'Rating' + | 'Single select' + | 'Tags' + | 'Text' + | 'Time' + | 'Timeline' + | 'Voting'; + /** * The standardized pageable format returned by much of the * Favro API {@see https://favro.com/developer/#pagination }. @@ -45,9 +69,9 @@ export namespace FavroApi { */ export namespace Organization { /** - * Helper types for the values of {@link Organization.Data} fields. + * Helper types for the values of {@link Organization.Model} fields. */ - export namespace FieldTypes { + export namespace ModelFieldValue { export type Role = | 'administrator' | 'fullMember' @@ -64,19 +88,19 @@ export namespace FavroApi { /** * The data model returned by the Favro API for Organizations. */ - export interface Data { + export interface Model { organizationId: string; name: string; - sharedToUsers: FieldTypes.Member[]; + sharedToUsers: ModelFieldValue.Member[]; } } - /** Favro API Data model for Collections. */ + /** Favro API Model model for Collections. */ export namespace Collection { /** - * Helper types for the values of {@link Collection.Data} fields. + * Helper types for the values of {@link Collection.Model} fields. */ - export namespace FieldTypes { + export namespace ModelFieldValue { export type ColorBackground = | 'purple' | 'green' @@ -99,7 +123,7 @@ export namespace FavroApi { } /** {@link https://favro.com/developer/#collections} */ - export interface Data { + export interface Model { /** The id of the collection. */ collectionId: string; /** The id of the organization that this collection exists in. */ @@ -107,11 +131,11 @@ export namespace FavroApi { /** The name of the collection. */ name: string; /** The array of collection members that the collection is shared to. */ - sharedToUsers: FieldTypes.Member[]; + sharedToUsers: ModelFieldValue.Member[]; /** The collection public sharing level. */ - publicSharing: FieldTypes.Visibility; + publicSharing: ModelFieldValue.Visibility; /** The collection background. */ - background: FieldTypes.ColorBackground; + background: ModelFieldValue.ColorBackground; /** Whether or not the collection is archived. */ archived: boolean; /** Whether or not full members shared this collection can create new widgets. */ @@ -119,17 +143,17 @@ export namespace FavroApi { } } - /** Favro API Data models for Users */ + /** Favro API Model models for Users */ export namespace User { /** - * Helper types for the values of {@link User.Data} + * Helper types for the values of {@link User.Model} */ - export namespace FieldTypes {} - export interface Data { + export namespace ModelFieldValue {} + export interface Model { userId: string; name: string; email: string; - organizationRole: Organization.FieldTypes.Role; + organizationRole: Organization.ModelFieldValue.Role; } } @@ -137,7 +161,7 @@ export namespace FavroApi { * Favro tags are global to an organization. */ export namespace Tag { - export namespace FieldTypes { + export namespace ModelFieldValue { /** {@link https://favro.com/developer/#tag-colors} */ export type Color = | 'blue' @@ -156,22 +180,22 @@ export namespace FavroApi { /** * The data model for a Tag returned from the Favro API. * See {@link https://favro.com/developer/#tag} */ - export interface Data { + export interface Model { tagId: string; organizationId: string; name: string; - color: FieldTypes.Color; + color: ModelFieldValue.Color; } } /** - * Favro API Data models for Widgets (a.k.a. "Boards") + * Favro API Model models for Widgets (a.k.a. "Boards") */ export namespace Widget { /** - * Helper types for the values of {@link Widget.Data} fields. + * Helper types for the values of {@link Widget.Model} fields. */ - export namespace FieldTypes { + export namespace ModelFieldValue { export type Color = | 'blue' | 'lightgreen' @@ -191,7 +215,7 @@ export namespace FavroApi { /** * The data model for a Widget returned from the Favro API. */ - export interface Data { + export interface Model { /** The shared id of the widget. */ widgetCommonId: string; /** The id of the organization that this widget exists in. */ @@ -201,15 +225,15 @@ export namespace FavroApi { /** The name of the widget. */ name: string; /** The type of the widget. */ - type: FieldTypes.Type; + type: ModelFieldValue.Type; /** If set, this means that this widget is a breakdown of a card. */ breakdownCardCommonId: string; /** The color of the widget icon. Refer to widget colors. */ - color: FieldTypes.Color; + color: ModelFieldValue.Color; /** The users that have ownership of the widget. */ - ownerRole: FieldTypes.Role; + ownerRole: ModelFieldValue.Role; /** The users that can add, edit and move cards on the widget. */ - editRole: FieldTypes.Role; + editRole: ModelFieldValue.Role; } /** @@ -218,19 +242,19 @@ export namespace FavroApi { * See {@link https://favro.com/developer/#update-a-widget} */ export type Create = { collectionId: string } & Partial< - Pick + Pick >; } /** - * Favro API Data models for Columns + * Favro API Model models for Columns */ export namespace Column { /** * The data model for Columns returned from the Favro API. * {@link https://favro.com/developer/#column-object} */ - export interface Data { + export interface Model { /** The id of the column. */ columnId: string; /** The id of the organization that this column exists in. */ @@ -249,4 +273,550 @@ export namespace FavroApi { estimationSum: number; } } + + /** + * Favro API Model models for Custom Field Definitions. + * See {@link FavroApi.CustomFieldValue} for how values are set + * on Cards. + * + * 📄 https://favro.com/developer/#custom-field-types + */ + export namespace CustomFieldDefinition { + /** + * @alias {@link CustomFieldType} + */ + export type Type = CustomFieldType; + + /** + * Helper types for the values of {@link CustomFieldDefinition.Model} fields. + * + * 📄 https://favro.com/developer/#custom-field-types + */ + export namespace ModelFieldValue { + /** + * @alias {@link CustomFieldType} + */ + export type FieldType = Type; + + /** + * For 'Single select' and 'Multiple select' fields, + * options are stored in the Custom Field definition + */ + export interface SelectOption { + /** The id of the custom field item. */ + customFieldItemId: string; + /** The name of the custom field item. */ + name: string; + } + } + + /** + * The Favro API data model for a Custom Field + * + * {@link https://favro.com/developer/#custom-field} + */ + export interface Model< + FieldType extends CustomFieldDefinition.ModelFieldValue.FieldType = any, + > { + /** The id of the organization that this custom field exists in. */ + organizationId: string; + /** The id of the custom field. */ + customFieldId: string; + /** Custom field type. */ + type: FieldType; + /** The name of the custom field. */ + name: string; + /** True if the custom field is currently enabled for the organization. */ + enabled: boolean; + /** The list of items that this custom field can have in case it is a selectable one. */ + customFieldItems: FieldType extends Extract< + CustomFieldType, + 'Single select' | 'Multiple select' + > + ? ModelFieldValue.SelectOption[] + : undefined; + } + } + + /** + * Favro API Model models for Custom Field Values, + * when they are set on a {@link Card}. For the *definitions* + * of Custom Fields, see {@link CustomFieldDefinition}. + * + * 📄 https://favro.com/developer/#card-custom-fields + */ + export namespace CustomFieldValue { + /** + * @alias {@link CustomFieldType} + */ + export type Type = CustomFieldType; + + /** + * Helper types for the values of {@link CustomFieldValue.Model} + * fields. + * 📄 https://favro.com/developer/#card-custom-fields + */ + export namespace ModelFieldValue { + export type Rating = 0 | 1 | 2 | 3 | 4 | 5; + } + + /** + * Data models for Custom Field Values returned from the Favro API, + * based on the {@link CustomFieldType} of that field. + */ + export interface Models { + Number: { + customFieldId: string; + /** The total value of the field. */ + total: number; + }; + Time: { + customFieldId: string; + /** The total value of all time reported by all users. */ + total: number; + /** + * The values reported by each user. + * The object key represents the userId of the user. + * The value is an array of user entries. Refer to custom field time user reports. */ + reports: { + [userId: string]: { + /** The id of the user entry. */ + reportId: string; + /** The user entry value. Passing 0 will remove report entry. For custom fields with type "Time", this value is in milliseconds. */ + value: number; + /** The description of the time report entry. */ + description: string; + /** The report date in ISO format. */ + createdAt: string; + }[]; + }; + }; + Text: { + customFieldId: string; + /** The value of the field. */ + value: string; + }; + Rating: { + customFieldId: string; + /** The value of the field. Valid value is integer from 0 to 5. */ + total: ModelFieldValue.Rating; + }; + Voting: { + customFieldId: string; + /** The id array of users that vote for the field. */ + value: string[]; + }; + Checkbox: { + customFieldId: string; + /** The value of the field. */ + value: boolean; + }; + Date: { + customFieldId: string; + /** The date value in ISO format. */ + value: string; + }; + Timeline: { + customFieldId: string; + /** The value options of the field. See custom field timeline. */ + timeline: { + /** The value of start date in ISO format. Required. */ + startDate: string; + /** The value of due date in ISO format. Required. */ + dueDate: string; + /** The value to determine display text of field should include time or not. */ + showTime: boolean; + }; + }; + Link: { + customFieldId: string; + /** The value options of the field. See custom field link. */ + link: { + /** The url of the field. Required. */ + url: string; + /** The display text of the field.*/ + text: string; + }; + }; + Members: { + customFieldId: string; + /** The id array of users that are assigned to card. */ + value: string[]; + }; + Tags: { + customFieldId: string; + /** The id array of tags that are added to card. */ + value: string[]; + }; + 'Single select': { + customFieldId: string; + /** The id array of item that are added to card. */ + value: string[]; + }; + 'Multiple select': { + customFieldId: string; + /** The id array of item that are added to card. */ + value: string[]; + }; + } + + /** + * The Favro API data model for a specific Custom Field Value, + * based on its type. + * + * @example + * // Create an alias for a specific type of Custom Field Value + * type CustomFieldValueSingleSelect = + * FavroApi.CustomFieldValue.Model<'Single select'>; + * + * // Or keep things generic for narrowing later + * function somethingGeneric< + * Type extends FavroApi.CustomFieldValue.Type + * >(customFieldValue: FavorApi.CustomFieldValue.Model) { + * // ... do stuff + * } + */ + export type Model = + Models[FieldType]; + + export type UpdateBody = + UpdateBodies[FieldType]; + + /** + * Data structures used to update Custom Field values on cards. + * These are not used by themselves; they are sent as an array + * of such structures when {@link Card.UpdateBody updating a card}, + * via the `customFields` field. + * + * 📄 https://favro.com/developer/#card-custom-field-parameters + */ + export interface UpdateBodies + extends Omit< + FavroApi.CustomFieldValue.Models, + 'Members' | 'Voting' | 'Time' | 'Tags' + > { + Members: { + customFieldId: string; + members: { + /** The list of members, that will be added to the card custom field (array of userIds).*/ + addUserIds: string[]; + /** The list of members, that will be removed from card custom field (array of userIds).*/ + removeUserIds: string[]; + /** The list of card assignment, that will update their statuses on the custom field accordingly.*/ + completeUsers: { userId: string; completed: boolean }[]; + }; + }; + Tags: { + customFieldId: string; + tags: { + /** The list of tag names or card tags that will be added to this card custom field. If the tag does not exist in the organization it will be created.*/ + addTags: string[]; + /** A list of tagIds that will be added to this card custom field.*/ + addTagIds: string[]; + /** The list of tag names, that will be removed from this card custom field.*/ + removeTags: string[]; + /** The list of tag IDs, that will be removed from this card custom field.*/ + removeTagIds: string[]; + }; + }; + Voting: { + customFieldId: string; + /** + * The value to determine the field should be either voted or unvoted. + */ + value: boolean; + }; + /** @private */ + Time: { + customFieldId: string; + }; + } + } + + /** + * Cards are the the unit of getting work done. The + * data returned from the Favro API consists of + * Widget-specific Card "instances", with some + * Card-global common properties, some common but Widget-specific + * properties, and Card-global {@link CustomFieldValue Custom Field values}. + * + * 📄 https://favro.com/developer/#card + */ + export namespace Card { + /** + * Helper types for the values of {@link Card.Model} fields. + */ + export namespace ModelFieldValue { + /** 📄 https://favro.com/developer/#card-assignment */ + export interface Assignment { + userId: string; + completed: boolean; + } + + /** 📄 https://favro.com/developer/#card-attachment */ + export interface Attachment { + name: string; + fileURL: string; + thumbnailURL?: string; + } + /** 📄 https://favro.com/developer/#card-time-on-board */ + export interface TimeOnBoard { + /** + * @remarks documentation does not include units + */ + time: number; + isStopped: boolean; + } + + /** 📄 https://favro.com/developer/#card-favro-attachment */ + export interface FavroAttachment { + /** + * The cardCommonId of card or widgetCommonId of widget that + * is linked to the card. */ + itemCommonId: string; + type: 'card' | FavroApi.Widget.ModelFieldValue.Type; + } + } + + /** + * The data model for a Card returned from the Favro API. + * + * 📄 https://favro.com/developer/#card + */ + export interface Model { + /** + * The id of the card. */ + cardId: string; + /** + * The id of the organization that this card exists in. */ + organizationId: string; + /** + * The shared id of the widget that this card exists on. + * Only returned if the card does not exist in a todo list. */ + widgetCommonId?: string; + /** + * The user id of the user of the todo list that this card exists in. + * Only returned if the card exists in a todo list. Otherwise + * widgetCommonId will be returned. */ + todoListUserId?: string; + /** + * Returns 'true' if the card exists in a todo list and has + * been completed by that user. */ + todoListCompleted?: boolean; + /** + * The id of the Kanban column that this card exists in. + * Only returned if the card exists on a widget. */ + columnId: string; + /** + * The id of the lane that this card exists in. + * Only returned if the card exists on a widget and the widget has lanes enabled. */ + laneId: string | null; + /** + * The id of the parent card in the card hierarchy (sheet or card list). + * Only returned if the card exists in a widget and is the child of another card. */ + parentCardId: string | null; + /** + * Returns 'true' if the card is a lane. */ + isLane: boolean; + /** + * Returns 'true' if the card is archived. */ + archived: boolean; + /** + * @deprecated + * Deprecated. Mapped to listPosition for right-pane widgets, + * and to sheetPosition for left-pane widgets. */ + position: number; + /** + * Position of the card in a column on a Kanban board, or in a todo list. */ + listPosition: number; + /** + * Position of the card in a hierarchical view (sheet or card list). */ + sheetPosition: number; + /** + * A shared id for all instances of this card in the organization. */ + cardCommonId: string; + /** + * The name of the card. */ + name: string; + /** + * The detailed description of the card. */ + detailedDescription: string; + /** + * The tags that are set on the card. + * TODO: Find out if these are strings or objects + */ + tags: string[]; + /** + * The sequentialId of the card. Useful for creating human readable links. */ + sequentialId: number; + /** + * The start date of the card. + */ + startDate: string | null; + /** + * The due date of the card. + */ + dueDate: string | null; + /** + * The users assigned to the card and whether or not they have + * completed the card. */ + assignments: ModelFieldValue.Assignment[]; + /** + * The number of comments posted on the card. */ + numComments: number; + /** + * The number of tasks on the card. */ + tasksTotal: number; + /** + * The number of tasks completed on the card. */ + tasksDone: number; + /** + * The file attachments on the card. */ + attachments: ModelFieldValue.Attachment[]; + /** + * The custom fields that are set on the card and enabled in + * the organization. */ + customFields: CustomFieldValue.Model[]; + /** + * The amount of time card has been on current board. */ + timeOnBoard: ModelFieldValue.TimeOnBoard; + /** + * The detailed summary of time card has been on each column of + * the current board. The object key represents the columnId of + * the column, and the value is the amount of time card has + * been on that column. */ + timeOnColumns: { [columnId: string]: number }; + /** The Favro attachments on the card. See card favro attachment. */ + favroAttachments: ModelFieldValue.FavroAttachment[]; + } + + /** + * Body parameters for API requests to create a Card. + * + * 📄 https://favro.com/developer/#create-a-card + */ + export interface CreateBody + extends Partial> { + name: string; + /** + * Require posting a card to a Widget (the API allows *not* doing + * that, but that seems like it would create more trouble than it's worth) */ + widgetCommonId: string; + descriptionFormat?: 'plaintext' | 'markdown'; + } + + /** + * Query parameters for API requests to list Cards. + * At least one of `cardCommonId`, `collectionId`, + * `widgetCommonId`, `cardSequentialId` must be specified. + * + * 📄 https://favro.com/developer/#get-all-cards + * + * @remarks The `todoList` option is not provided since Bravo does + * not meaningfully support it. + */ + export interface SearchQuery { + cardCommonId?: string; + widgetCommonId?: string; + collectionId?: string; + cardSequentialId?: string | number; + + /** Limit the search scope to a specific column */ + columnId?: string; + /** + * Cards can be returned multiple times + * in a single request, but as per-Widget "instances", + * since they can live in multiple + * places depending on scope of the search. + */ + unique?: boolean; + /** + * The API defaults to `'plaintext'` for some reason... + */ + descriptionFormat?: 'plaintext' | 'markdown'; + } + + /** + * Body parameters for API requests to update a Card. + * Note that `cardId` must be specified in the URL, + * and `descriptionFormat` should be set to `'markdown'` + * in the query params so that the returned result will + * have a Markdown body. + * + * 📄 https://favro.com/developer/#update-a-card + */ + export interface UpdateBody { + /** The name of the card. */ + name?: string; + /** The detailed description of the card. Supports formatting. */ + detailedDescription?: string; + /** The widgetCommonId to commit the card in (if adding to a Widget). */ + widgetCommonId?: string; + /** The columnId to commit the card in. It must belong to the widget specified in the widgetCommonId parameter. */ + columnId?: string; + /** The laneId to commit the card in. This is only applicable if creating the card on a widget that has lanes enabled. */ + laneId?: string; + /** 'commit' to commit card, 'move' to move card. 'commit' by default. */ + dragMode?: 'commit' | 'move'; + /** + * @deprecated + * Mapped to listPosition for right-pane widgets, and to sheetPosition for left-pane widgets. + */ + position?: number; + /** New position of the card in a column on a Kanban board, or in a todo list. */ + listPosition?: number; + /** New position of the card in a hierarchical view (sheet or card list).*/ + sheetPosition?: number; + /** The id of the parent card in the card hierarchy (sheet or card list), where the card will be commited. It must belong to the widget specified in the widgetCommonId parameter. */ + parentCardId?: string; + /** The list of assignments, that will be added to card (array of userIds).*/ + addAssignmentIds?: string[]; + /** The list of assignments, that will be removed from card (array of userIds).*/ + removeAssignmentIds?: string[]; + /** The list of card assignment, that will update their statuses accordingly.*/ + completeAssignments?: { userId: string; completed: boolean }[]; + /** The list of tag names or card tags that will be added to the card. If the tag does not exist in the organization it will be created.*/ + addTags?: string[]; + /** A list of tagIds that will be added to card.*/ + addTagIds?: string[]; + /** The list of tag names, that will be removed from card.*/ + removeTags?: string[]; + /** The list of tag IDs, that will be removed from card.*/ + removeTagIds?: string[]; + /** The start date of card. Format ISO-8601. If null, start date will be removed.*/ + startDate?: string | null; + /** The due date of card. Format ISO-8601. If null, due date will be removed.*/ + dueDate?: string | null; + /** The list of card tasklists, that will be added to card.*/ + addTasklists?: { + name: string; + tasks: (string | { name: string; completed?: boolean })[]; + }[]; + /** + * The list of card attachments URLs, that will be removed from the card. + * + * @remarks Unclear what URL is being referenced here. Best guess is the + * `fileURL` field from an attachment object. + */ + removeAttachments?: string[]; + /** + * The list of card custom field parameters, that will be added or modified. + * + * @remarks Value objects are VERY similar to those that appear in the same + * field upon fetching a Card, but there are some differences. + */ + customFields?: CustomFieldValue.UpdateBody[]; + /** The list of card favro attachment that will be added to the card.*/ + addFavroAttachments?: FavroApi.Card.ModelFieldValue.FavroAttachment[]; + /** + * The list of cardCommonId and widgetCommonId of card + * favro attachment, that will be removed from the card. + * + * @remarks Presumably this is the `itemCommonId` field value + */ + removeFavroAttachmentIds?: string[]; + /** Archive or unarchive card.*/ + archive?: boolean; + } + } } diff --git a/src/types/FavroCardTypes.ts b/src/types/FavroCardTypes.ts deleted file mode 100644 index ac858ed..0000000 --- a/src/types/FavroCardTypes.ts +++ /dev/null @@ -1,337 +0,0 @@ -import type { FavroApi } from '$favro'; - -export type OptionFavroDescriptionFormat = 'plaintext' | 'markdown'; - -export type FavroApiParamsCardCreate = Partial< - Omit -> & { - name: string; - /** - * Require posting a card to a Widget (the API allows *not* doing - * that, but that seems like it would create more trouble than it's worth) */ - widgetCommonId: string; - descriptionFormat?: OptionFavroDescriptionFormat; -}; - -export interface FavroApiGetCardsBase { - /** Limit the search scope to a specific column */ - columnId?: string; - /** - * Cards can be returned multiple times - * in a single request, but as per-Widget "instances", - * since they can live in multiple - * places depending on scope of the search. - */ - unique?: boolean; - /** - * The API defaults to `'plaintext'` for some reason... - */ - descriptionFormat?: OptionFavroDescriptionFormat; -} - -export interface FavroApiGetCardsByCardCommonId extends FavroApiGetCardsBase { - cardCommonId: string; -} - -export interface FavroApiGetCardsByWidgetCommonId extends FavroApiGetCardsBase { - widgetCommonId: string; -} - -export interface FavroApiGetCardsByCollectionId extends FavroApiGetCardsBase { - collectionId: string; -} - -export interface FavroApiGetCardsBySequentialId extends FavroApiGetCardsBase { - cardSequentialId: string | number; -} - -export type FavroApiGetCardsParams = - | FavroApiGetCardsByCardCommonId - | FavroApiGetCardsByWidgetCommonId - | FavroApiGetCardsByCollectionId - | FavroApiGetCardsBySequentialId; - -// FOR SETTING VALUES ON A CARD - -export type DataFavroCustomFieldType = - | 'Number' - | 'Time' - | 'Text' - | 'Rating' - | 'Voting' - | 'Checkbox' - | 'Date' - | 'Timeline' - | 'Link' - | 'Members' - | 'Tags' - | 'Single select' - | 'Multiple select'; - -export type DataFavroCustomFieldsValues = { - Number: DataFavroCardFieldNumber; - Time: DataFavroCardFieldTime; - Text: DataFavroCardFieldText; - Rating: DataFavroCardFieldRating; - Voting: DataFavroCardFieldVote; - Checkbox: DataFavroCardFieldCheckbox; - Date: DataFavroCardFieldDate; - Timeline: DataFavroCardFieldTimeline; - Link: DataFavroCardFieldLink; - Members: DataFavroCardFieldMembers; - Tags: DataFavroCardFieldTags; - 'Single select': DataFavroCardFieldSingleSelect; - 'Multiple select': DataFavroCardFieldMultipleSelect; -}; - -/** - * {@link https://favro.com/developer/#card-custom-fields} - */ -export type DataFavroCardCustomField = { customFieldId: string } & ( - | DataFavroCardFieldCheckbox - | DataFavroCardFieldDate - | DataFavroCardFieldLink - | DataFavroCardFieldMembers - | DataFavroCardFieldMultipleSelect - | DataFavroCardFieldNumber - | DataFavroCardFieldRating - | DataFavroCardFieldSingleSelect - | DataFavroCardFieldTags - | DataFavroCardFieldText - | DataFavroCardFieldTime - | DataFavroCardFieldTimeline - | DataFavroCardFieldVote -); - -export interface DataFavroCardFieldNumber { - /** The total value of the field. */ - total: number; -} - -export type DataFavroRating = 0 | 1 | 2 | 3 | 4 | 5; - -export interface DataFavroCardFieldTimeUserReport { - /** The id of the user entry. */ - reportId: string; - /** The user entry value. Passing 0 will remove report entry. For custom fields with type "Time", this value is in milliseconds. */ - value: number; - /** The description of the time report entry. */ - description: string; - /** The report date in ISO format. */ - createdAt: string; -} -export interface DataFavroCardFieldTime { - /** The total value of all time reported by all users. */ - total: number; - /** - * The values reported by each user. - * The object key represents the userId of the user. - * The value is an array of user entries. Refer to custom field time user reports. */ - reports: { - [userId: string]: DataFavroCardFieldTimeUserReport[]; - }; -} -export interface DataFavroCardFieldText { - /** The value of the field. */ - value: string; -} -export interface DataFavroCardFieldRating { - /** The value of the field. Valid value is integer from 0 to 5. */ - total: DataFavroRating; -} -export interface DataFavroCardFieldVote { - /** The id array of users that vote for the field. */ - value: string[]; -} -export interface DataFavroCardFieldCheckbox { - /** The value of the field. */ - value: boolean; -} -export interface DataFavroCardFieldDate { - /** The date value in ISO format. */ - value: string; -} -export interface DataFavroCardFieldTimeline { - /** The value options of the field. See custom field timeline. */ - timeline: { - /** The value of start date in ISO format. Required. */ - startDate: string; - /** The value of due date in ISO format. Required. */ - dueDate: string; - /** The value to determine display text of field should include time or not. */ - showTime: boolean; - }; -} -export interface DataFavroCardFieldLink { - /** The value options of the field. See custom field link. */ - link: { - /** The url of the field. Required. */ - url: string; - /** The display text of the field.*/ - text: string; - }; -} -interface DataFavroCardFieldMembers { - /** The id array of users that are assigned to card. */ - value: string[]; -} -interface DataFavroCardFieldTags { - /** The id array of tags that are added to card. */ - value: string[]; -} -export interface DataFavroCardFieldSingleSelect { - /** The id array of item that are added to card. */ - value: string[]; -} -export interface DataFavroCardFieldMultipleSelect { - /** The id array of item that are added to card. */ - value: string[]; -} - -/** {@link https://favro.com/developer/#card-assignment} */ -export interface DataFavroCardAssignment { - userId: string; - completed: boolean; -} - -/** {@link https://favro.com/developer/#card-attachment} */ -export interface DataFavroCardAttachment { - name: string; - fileURL: string; - thumbnailURL?: string; -} - -/** {@link https://favro.com/developer/#card-time-on-board} */ -interface DataFavroCardTimeOnBoard { - /** - * @remarks documentation does not include units - */ - time: number; - isStopped: boolean; -} - -/** {@link https://favro.com/developer/#card-favro-attachment} */ -export interface DataFavroCardFavroAttachment { - /** - * The cardCommonId of card or widgetCommonId of widget that - * is linked to the card. */ - itemCommonId: string; - type: 'card' | FavroApi.Widget.FieldTypes.Type; -} - -/** - * Cards are the key data unit in Favro. The docs are unclear - * about which fields are available in what contexts (they - * refer to "these optional fields" in the docs prior to all - * the params captured below as an interface). - * - * Trial and error will be required to figure it all out! - * - * {@link https://favro.com/developer/#card} - */ -export interface DataFavroCard { - /** - * The id of the card. */ - cardId: string; - /** - * The id of the organization that this card exists in. */ - organizationId: string; - /** - * The shared id of the widget that this card exists on. - * Only returned if the card does not exist in a todo list. */ - widgetCommonId?: string; - /** - * The user id of the user of the todo list that this card exists in. - * Only returned if the card exists in a todo list. Otherwise - * widgetCommonId will be returned. */ - todoListUserId?: string; - /** - * Returns 'true' if the card exists in a todo list and has - * been completed by that user. */ - todoListCompleted?: boolean; - /** - * The id of the Kanban column that this card exists in. - * Only returned if the card exists on a widget. */ - columnId: string; - /** - * The id of the lane that this card exists in. - * Only returned if the card exists on a widget and the widget has lanes enabled. */ - laneId: string | null; - /** - * The id of the parent card in the card hierarchy (sheet or card list). - * Only returned if the card exists in a widget and is the child of another card. */ - parentCardId: string | null; - /** - * Returns 'true' if the card is a lane. */ - isLane: boolean; - /** - * Returns 'true' if the card is archived. */ - archived: boolean; - /** - * @deprecated - * Deprecated. Mapped to listPosition for right-pane widgets, - * and to sheetPosition for left-pane widgets. */ - position: number; - /** - * Position of the card in a column on a Kanban board, or in a todo list. */ - listPosition: number; - /** - * Position of the card in a hierarchical view (sheet or card list). */ - sheetPosition: number; - /** - * A shared id for all instances of this card in the organization. */ - cardCommonId: string; - /** - * The name of the card. */ - name: string; - /** - * The detailed description of the card. */ - detailedDescription: string; - /** - * The tags that are set on the card. - * TODO: Find out if these are strings or objects - */ - tags: string[]; - /** - * The sequentialId of the card. Useful for creating human readable links. */ - sequentialId: number; - /** - * The start date of the card. - */ - startDate: string | null; - /** - * The due date of the card. - */ - dueDate: string | null; - /** - * The users assigned to the card and whether or not they have - * completed the card. */ - assignments: DataFavroCardAssignment[]; - /** - * The number of comments posted on the card. */ - numComments: number; - /** - * The number of tasks on the card. */ - tasksTotal: number; - /** - * The number of tasks completed on the card. */ - tasksDone: number; - /** - * The file attachments on the card. */ - attachments: DataFavroCardAttachment[]; - /** - * The custom fields that are set on the card and enabled in - * the organization. */ - customFields: DataFavroCardCustomField[]; - /** - * The amount of time card has been on current board. */ - timeOnBoard: DataFavroCardTimeOnBoard; - /** - * The detailed summary of time card has been on each column of - * the current board. The object key represents the columnId of - * the column, and the value is the amount of time card has - * been on that column. */ - timeOnColumns: { [columnId: string]: number }; - /** The Favro attachments on the card. See card favro attachment. */ - favroAttachments: DataFavroCardFavroAttachment[]; -} diff --git a/src/types/FavroCardUpdateTypes.ts b/src/types/FavroCardUpdateTypes.ts deleted file mode 100644 index 5ffbe0c..0000000 --- a/src/types/FavroCardUpdateTypes.ts +++ /dev/null @@ -1,189 +0,0 @@ -import type { - DataFavroCardFieldNumber, - DataFavroCardFieldText, - DataFavroCardFieldSingleSelect, - DataFavroCardFieldMultipleSelect, - DataFavroCardFieldCheckbox, - DataFavroCardFieldDate, - DataFavroCardFieldLink, - DataFavroCardFieldRating, - DataFavroCardFieldTimeline, - DataFavroCardFavroAttachment, - DataFavroCardFieldTimeUserReport, - DataFavroCustomFieldType, -} from './FavroCardTypes'; -import type { ExtractKeysByValue } from './Utility.js'; - -export interface DataFavroCardFieldMembersUpdate { - members: { - /** The list of members, that will be added to the card custom field (array of userIds).*/ - addUserIds: string[]; - /** The list of members, that will be removed from card custom field (array of userIds).*/ - removeUserIds: string[]; - /** The list of card assignment, that will update their statuses on the custom field accordingly.*/ - completeUsers: { userId: string; completed: boolean }[]; - }; -} -interface DataFavroCardFieldTagsUpdate { - tags: { - /** The list of tag names or card tags that will be added to this card custom field. If the tag does not exist in the organization it will be created.*/ - addTags: string[]; - /** A list of tagIds that will be added to this card custom field.*/ - addTagIds: string[]; - /** The list of tag names, that will be removed from this card custom field.*/ - removeTags: string[]; - /** The list of tag IDs, that will be removed from this card custom field.*/ - removeTagIds: string[]; - }; -} - -interface DataFavroCardFieldTimeUpdate { - /** The user report to be added. See custom field time user reports. Optional. */ - addUserReports: DataFavroCardFieldTimeUserReport; - /** The user report to be updated. See custom field time user reports. Optional. */ - updateUserReports: DataFavroCardFieldTimeUserReport; - /** The user report to be removed. See custom field time user reports. Optional */ - removeUserReports: DataFavroCardFieldTimeUserReport; -} - -interface DataFavroCardFieldVotingUpdate { - /** - * The value to determine the field should be either voted or unvoted. - * - * @remarks It's unclear if this refers to the CURRENT user (the one who - * ownes the API token). Presumably it does? - */ - value: boolean; -} - -/** - * These strongly overlap, but are not identical to, the CustomField - * object shapes returned by the API. - * - * {@link https://favro.com/developer/#card-custom-field-parameters} - */ -export type FavroApiParamsCardUpdateCustomField< - FieldType extends DataFavroCustomFieldType = any, -> = { - customFieldId: string; -} & FavroApiParamsCardCustomField; - -export type FavroApiParamsCardCustomField< - FieldType extends DataFavroCustomFieldType = any, -> = FavroApiParamsCardCustomFields[FieldType]; - -/** - * Map of Custom Field Types to their respective - * update object shapes. - */ -type FavroApiParamsCardCustomFields = { - Checkbox: DataFavroCardFieldCheckbox; - Date: DataFavroCardFieldDate; - Link: DataFavroCardFieldLink; - 'Multiple select': DataFavroCardFieldMultipleSelect; - Number: DataFavroCardFieldNumber; - Rating: DataFavroCardFieldRating; - 'Single select': DataFavroCardFieldSingleSelect; - Text: DataFavroCardFieldText; - Timeline: DataFavroCardFieldTimeline; - Members: DataFavroCardFieldMembersUpdate; - Tags: DataFavroCardFieldTagsUpdate; - Time: DataFavroCardFieldTimeUpdate; - Voting: DataFavroCardFieldVotingUpdate; -}; - -export type FavroApiParamsCardUpdateField = keyof FavroApiParamsCardUpdate; - -/** - * Fields for a Card update request whose values are arrays. - * - * Useful for generic methods that act on arrays. - */ -export type FavroApiParamsCardUpdateArrayField = ExtractKeysByValue< - Required, - any[] ->; - -/** - * Body parameters for updating a Card. - * - * Note that `cardId` must be specified in the URL, - * and `descriptionFormat` should be set to `'markdown'` - * in the query params so that the returned result will - * have a Markdown body. - * - * {@link https://favro.com/developer/#update-a-card} - */ -export interface FavroApiParamsCardUpdate { - /** The name of the card. */ - name?: string; - /** The detailed description of the card. Supports formatting. */ - detailedDescription?: string; - /** The widgetCommonId to commit the card in (if adding to a Widget). */ - widgetCommonId?: string; - /** The columnId to commit the card in. It must belong to the widget specified in the widgetCommonId parameter. */ - columnId?: string; - /** The laneId to commit the card in. This is only applicable if creating the card on a widget that has lanes enabled. */ - laneId?: string; - /** 'commit' to commit card, 'move' to move card. 'commit' by default. */ - dragMode?: 'commit' | 'move'; - /** - * @deprecated - * Mapped to listPosition for right-pane widgets, and to sheetPosition for left-pane widgets. - */ - position?: number; - /** New position of the card in a column on a Kanban board, or in a todo list. */ - listPosition?: number; - /** New position of the card in a hierarchical view (sheet or card list).*/ - sheetPosition?: number; - /** The id of the parent card in the card hierarchy (sheet or card list), where the card will be commited. It must belong to the widget specified in the widgetCommonId parameter. */ - parentCardId?: string; - /** The list of assignments, that will be added to card (array of userIds).*/ - addAssignmentIds?: string[]; - /** The list of assignments, that will be removed from card (array of userIds).*/ - removeAssignmentIds?: string[]; - /** The list of card assignment, that will update their statuses accordingly.*/ - completeAssignments?: { userId: string; completed: boolean }[]; - /** The list of tag names or card tags that will be added to the card. If the tag does not exist in the organization it will be created.*/ - addTags?: string[]; - /** A list of tagIds that will be added to card.*/ - addTagIds?: string[]; - /** The list of tag names, that will be removed from card.*/ - removeTags?: string[]; - /** The list of tag IDs, that will be removed from card.*/ - removeTagIds?: string[]; - /** The start date of card. Format ISO-8601. If null, start date will be removed.*/ - startDate?: string | null; - /** The due date of card. Format ISO-8601. If null, due date will be removed.*/ - dueDate?: string | null; - /** The list of card tasklists, that will be added to card.*/ - addTasklists?: { - name: string; - tasks: (string | { name: string; completed?: boolean })[]; - }[]; - /** - * The list of card attachments URLs, that will be removed from the card. - * - * @remarks Unclear what URL is being referenced here. Best guess is the - * `fileURL` field from an attachment object. - */ - removeAttachments?: string[]; - /** - * The list of card custom field parameters, that will be added or modified. - * - * @remarks Value objects are VERY similar to those that appear in the same - * field upon fetching a Card, but there are some differences. - */ - customFields?: FavroApiParamsCardUpdateCustomField[]; - /** The list of card favro attachment that will be added to the card.*/ - addFavroAttachments?: DataFavroCardFavroAttachment[]; - /** - * The list of cardCommonId and widgetCommonId of card - * favro attachment, that will be removed from the card. - * - * @remarks Presumably this is the `itemCommonId` field value - */ - removeFavroAttachmentIds?: string[]; - /** Archive or unarchive card.*/ - archive?: boolean; -} diff --git a/src/types/FavroCustomFieldTypes.ts b/src/types/FavroCustomFieldTypes.ts deleted file mode 100644 index 17d361f..0000000 --- a/src/types/FavroCustomFieldTypes.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @remarks - * The documentation for the custom fields is a bit - * unclear. The types below match the docs, but should be - * checked for validity against real returned data. In - * particular, some types refer to a "custom" object for - * that same type -- it's unclear under what circumstances - * the parent vs child objects are returned by the Favro API. - * - * {@link https://favro.com/developer/#custom-field-types} - */ - -import type { DataFavroCustomFieldType } from './FavroCardTypes.js'; - -/** - * The definition data for a custom field. - * - * {@link https://favro.com/developer/#custom-field} - */ -export interface DataFavroCustomFieldDefinition< - FieldType extends DataFavroCustomFieldType = DataFavroCustomFieldType, -> { - /** The id of the organization that this custom field exists in. */ - organizationId: string; - /** The id of the custom field. */ - customFieldId: string; - /** Custom field type. */ - type: FieldType; - /** The name of the custom field. */ - name: string; - /** True if the custom field is currently enabled for the organization. */ - enabled: boolean; - /** The list of items that this custom field can have in case it is a selectable one. */ - customFieldItems: FieldType extends 'Single select' | 'Multiple select' - ? { - /** The id of the custom field item. */ - customFieldItemId: string; - /** The name of the custom field item. */ - name: string; - }[] - : undefined; -}