diff --git a/packages/capabilities/src/types.ts b/packages/capabilities/src/types.ts index 37c6c05e3..9848a42ca 100644 --- a/packages/capabilities/src/types.ts +++ b/packages/capabilities/src/types.ts @@ -49,6 +49,15 @@ export type CARLink = Link export type AccountDID = DID<'mailto'> export type SpaceDID = DID<'key'> +/** + * Error for cases where an interface implementation needs to return an + * error that isn't defined explicitly in the interface. + */ +export interface UnexpectedError extends Ucanto.Failure { + name: 'UnexpectedError' + cause: unknown +} + /** * failure due to a resource not having enough storage capacity. */ @@ -628,7 +637,7 @@ export interface PlanNotFound extends Ucanto.Failure { name: 'PlanNotFound' } -export type PlanGetFailure = PlanNotFound +export type PlanGetFailure = PlanNotFound | UnexpectedError export type PlanSet = InferInvokedCapability @@ -645,7 +654,14 @@ export interface InvalidPlanName extends Ucanto.Failure { name: 'InvalidPlanName' } -export type PlanSetFailure = CustomerNotFound +export interface PlanUpdateError extends Ucanto.Failure { + name: 'PlanUpdateError' +} + +export type PlanSetFailure = + | CustomerNotFound + | PlanUpdateError + | UnexpectedError // Top export type Top = InferInvokedCapability diff --git a/packages/upload-api/package.json b/packages/upload-api/package.json index ef19f62ea..157b171c0 100644 --- a/packages/upload-api/package.json +++ b/packages/upload-api/package.json @@ -30,6 +30,9 @@ "customer": [ "dist/src/customer.d.ts" ], + "plan": [ + "dist/src/plan.d.ts" + ], "provider": [ "dist/src/provider.d.ts" ], @@ -81,6 +84,10 @@ "types": "./dist/src/provider.d.ts", "import": "./src/provider.js" }, + "./plan": { + "types": "./dist/src/plan.d.ts", + "import": "./src/plan.js" + }, "./space": { "types": "./dist/src/space.d.ts", "import": "./src/space.js" diff --git a/packages/upload-api/src/types/plans.ts b/packages/upload-api/src/types/plans.ts index 6bfc06a12..4c82b3847 100644 --- a/packages/upload-api/src/types/plans.ts +++ b/packages/upload-api/src/types/plans.ts @@ -1,5 +1,13 @@ import * as Ucanto from '@ucanto/interface' -import { AccountDID, DID, PlanGetFailure, PlanGetSuccess, PlanSetFailure, PlanSetSuccess } from '../types.js' +import { + AccountDID, + DID, + PlanGetFailure, + PlanGetSuccess, + PlanSetFailure, + PlanSetSuccess, + UnexpectedError, +} from '../types.js' export type PlanID = DID @@ -7,7 +15,7 @@ export interface CustomerExists extends Ucanto.Failure { name: 'CustomerExists' } -type PlanInitializeFailure = CustomerExists +type PlanInitializeFailure = CustomerExists | UnexpectedError /** * Stores subscription plan information. @@ -16,10 +24,10 @@ export interface PlansStorage { /** * Initialize a customer in our system, tracking the external billing * system ID and the plan they have chosen. - * + * * Designed to be use from, eg, a webhook handler for an account creation event * in a third party billing system. - * + * * @param account account DID * @param billingID ID used by billing system to track this account * @param plan the ID of the initial plan diff --git a/packages/upload-api/test/storage/plans-storage-tests.js b/packages/upload-api/test/storage/plans-storage-tests.js index 00dda9661..ac403102e 100644 --- a/packages/upload-api/test/storage/plans-storage-tests.js +++ b/packages/upload-api/test/storage/plans-storage-tests.js @@ -11,7 +11,11 @@ export const test = { 'can initialize a customer': async (assert, context) => { const storage = context.plansStorage - const initializeResult = await storage.initialize(account, billingID, product) + const initializeResult = await storage.initialize( + account, + billingID, + product + ) assert.ok(initializeResult.ok) @@ -19,7 +23,10 @@ export const test = { assert.equal(getResult.ok?.product, product) }, - 'should not allow plans to be updated for uninitialized customers': async (assert, context) => { + 'should not allow plans to be updated for uninitialized customers': async ( + assert, + context + ) => { const storage = context.plansStorage const setResult = await storage.set(account, product) @@ -28,10 +35,17 @@ export const test = { assert.equal(setResult.error?.name, 'CustomerNotFound') }, - 'should allow plans to be updated for initialized customers': async (assert, context) => { + 'should allow plans to be updated for initialized customers': async ( + assert, + context + ) => { const storage = context.plansStorage - const initializeResult = await storage.initialize(account, billingID, product) + const initializeResult = await storage.initialize( + account, + billingID, + product + ) assert.ok(initializeResult.ok) diff --git a/packages/upload-api/test/storage/plans-storage.js b/packages/upload-api/test/storage/plans-storage.js index 6a892ef88..a8bd07a20 100644 --- a/packages/upload-api/test/storage/plans-storage.js +++ b/packages/upload-api/test/storage/plans-storage.js @@ -13,16 +13,20 @@ export class PlansStorage { } /** - * - * @param {Types.AccountDID} account - * @param {string} billingID - * @param {Types.DID} product + * + * @param {Types.AccountDID} account + * @param {string} billingID + * @param {Types.DID} product */ async initialize(account, billingID, product) { if (this.plans[account]) { return { error: new CustomerExists(account) } } - this.plans[account] = { product, billingID, updatedAt: new Date().toISOString() } + this.plans[account] = { + product, + billingID, + updatedAt: new Date().toISOString(), + } return { ok: {} } }