Skip to content

Commit

Permalink
fix: one more tweak to the PlanStorage interface (#1280)
Browse files Browse the repository at this point in the history
Introduce `UnexpectedError` to allow implementations to return errors
that weren't anticipated in the interface definition.

More information about this problem here:

storacha/RFC#7 (comment)

Also in this PR:

1) export plan.js as a module so callers can use error classes defined
there
2) use `prettier` to make things prettier
  • Loading branch information
travis authored Jan 29, 2024
1 parent e3dfe17 commit 5a44565
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 15 deletions.
20 changes: 18 additions & 2 deletions packages/capabilities/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ export type CARLink = Link<unknown, typeof CAR.codec.code>
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.
*/
Expand Down Expand Up @@ -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<typeof PlanCaps.set>

Expand All @@ -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<typeof top>
Expand Down
7 changes: 7 additions & 0 deletions packages/upload-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"customer": [
"dist/src/customer.d.ts"
],
"plan": [
"dist/src/plan.d.ts"
],
"provider": [
"dist/src/provider.d.ts"
],
Expand Down Expand Up @@ -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"
Expand Down
16 changes: 12 additions & 4 deletions packages/upload-api/src/types/plans.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
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

export interface CustomerExists extends Ucanto.Failure {
name: 'CustomerExists'
}

type PlanInitializeFailure = CustomerExists
type PlanInitializeFailure = CustomerExists | UnexpectedError

/**
* Stores subscription plan information.
Expand All @@ -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
Expand Down
22 changes: 18 additions & 4 deletions packages/upload-api/test/storage/plans-storage-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@ 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)

const getResult = await storage.get(account)
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)
Expand All @@ -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)

Expand Down
14 changes: 9 additions & 5 deletions packages/upload-api/test/storage/plans-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {} }
}

Expand Down

0 comments on commit 5a44565

Please sign in to comment.