From a3a192d98c561b70194c54b7eaea24dbbc7122e9 Mon Sep 17 00:00:00 2001 From: Dan Billson Date: Thu, 21 Nov 2024 13:00:36 +0000 Subject: [PATCH] Customer Portal Sessions (#74) * feat: add customer portal session resource * chore: remove unused mock Co-authored-by: davidgrayston-paddle * test: add url assertion --------- Co-authored-by: davidgrayston-paddle --- CHANGELOG.md | 6 ++++ package.json | 2 +- .../customer-portal-sessions.mock.ts | 36 +++++++++++++++++++ .../customer-portal-sessions.test.ts | 32 +++++++++++++++++ .../customer-portal-session.ts | 22 ++++++++++++ .../customer-portal-subscription-url.ts | 19 ++++++++++ .../customer-portal-session/general.ts | 15 ++++++++ src/entities/customer-portal-session/index.ts | 8 +++++ src/entities/customer-portal-session/urls.ts | 21 +++++++++++ src/entities/index.ts | 1 + src/paddle.ts | 3 ++ .../customer-portal-sessions/index.ts | 34 ++++++++++++++++++ ...-customer-portal-session-request-object.ts | 9 +++++ .../operations/index.ts | 7 ++++ src/resources/index.ts | 1 + .../customer-portal-session-response.ts | 14 ++++++++ .../customer-portal-subscription-url.ts | 11 ++++++ src/types/customer-portal-session/general.ts | 9 +++++ src/types/customer-portal-session/index.ts | 10 ++++++ src/types/customer-portal-session/urls.ts | 13 +++++++ src/types/index.ts | 1 + 21 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/mocks/resources/customer-portal-sessions.mock.ts create mode 100644 src/__tests__/resources/customer-portal-sessions.test.ts create mode 100644 src/entities/customer-portal-session/customer-portal-session.ts create mode 100644 src/entities/customer-portal-session/customer-portal-subscription-url.ts create mode 100644 src/entities/customer-portal-session/general.ts create mode 100644 src/entities/customer-portal-session/index.ts create mode 100644 src/entities/customer-portal-session/urls.ts create mode 100644 src/resources/customer-portal-sessions/index.ts create mode 100644 src/resources/customer-portal-sessions/operations/create-customer-portal-session-request-object.ts create mode 100644 src/resources/customer-portal-sessions/operations/index.ts create mode 100644 src/types/customer-portal-session/customer-portal-session-response.ts create mode 100644 src/types/customer-portal-session/customer-portal-subscription-url.ts create mode 100644 src/types/customer-portal-session/general.ts create mode 100644 src/types/customer-portal-session/index.ts create mode 100644 src/types/customer-portal-session/urls.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index abb8d46..58ab25d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ When we make [non-breaking changes](https://developer.paddle.com/api-reference/a This means when upgrading minor versions of the SDK, you may notice type errors. You can safely ignore these or fix by adding additional type guards. +## 2.1.0 - 2024-11-21 + +### Added + +- Added `customerPortalSessions` resources + ## 2.0.0 - 2024-11-20 > **Breaking changes:** This version includes major improvements that introduce breaking changes. These are called out below. diff --git a/package.json b/package.json index 769191e..792a263 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paddle/paddle-node-sdk", - "version": "2.0.0", + "version": "2.1.0", "description": "A Node.js SDK that you can use to integrate Paddle Billing with applications written in server-side JavaScript.", "main": "dist/cjs/index.cjs.node.js", "module": "dist/esm/index.esm.node.js", diff --git a/src/__tests__/mocks/resources/customer-portal-sessions.mock.ts b/src/__tests__/mocks/resources/customer-portal-sessions.mock.ts new file mode 100644 index 0000000..841a8a4 --- /dev/null +++ b/src/__tests__/mocks/resources/customer-portal-sessions.mock.ts @@ -0,0 +1,36 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { ICustomerPortalSessionResponse } from '../../../types/index.js'; +import { Response, ResponsePaginated } from '../../../internal/index.js'; + +export const CustomerPortalSessionMock: ICustomerPortalSessionResponse = { + id: 'cpls_01h4ge9r64c22exjsx0fy8b48b', + customer_id: 'ctm_123', + urls: { + general: { + overview: + 'https://customer-portal.paddle.com/cpl_01j7zbyqs3vah3aafp4jf62qaw?action=overview&token=pga_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjdG1fMDFncm5uNHp0YTVhMW1mMDJqanplN3kyeXMiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3Mjc2NzkyMzh9._oO12IejzdKmyKTwb7BLjmiILkx4_cSyGjXraOBUI_g', + }, + subscriptions: [ + { + id: 'sub_123', + cancel_subscription: + 'https://customer-portal.paddle.com/cpl_01j7zbyqs3vah3aafp4jf62qaw?action=cancel_subscription&subscription_id=sub_01h04vsc0qhwtsbsxh3422wjs4&token=pga_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjdG1fMDFncm5uNHp0YTVhMW1mMDJqanplN3kyeXMiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3Mjc2NzkyMzh9._oO12IejzdKmyKTwb7BLjmiILkx4_cSyGjXraOBUI_g', + update_subscription_payment_method: + 'https://customer-portal.paddle.com/cpl_01j7zbyqs3vah3aafp4jf62qaw?action=update_subscription_payment_method&subscription_id=sub_01h04vsc0qhwtsbsxh3422wjs4&token=pga_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjdG1fMDFncm5uNHp0YTVhMW1mMDJqanplN3kyeXMiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3Mjc2NzkyMzh9._oO12IejzdKmyKTwb7BLjmiILkx4_cSyGjXraOBUI_g', + }, + ], + }, + created_at: '2024-10-25T06:53:58Z', +}; + +export const CustomerPortalSessionMockResponse: Response = { + data: CustomerPortalSessionMock, + meta: { + request_id: '', + }, +}; diff --git a/src/__tests__/resources/customer-portal-sessions.test.ts b/src/__tests__/resources/customer-portal-sessions.test.ts new file mode 100644 index 0000000..888cb0c --- /dev/null +++ b/src/__tests__/resources/customer-portal-sessions.test.ts @@ -0,0 +1,32 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { CustomerPortalSessionsResource } from '../../resources/index.js'; +import { getPaddleTestClient } from '../helpers/test-client.js'; +import { + CustomerPortalSessionMockResponse, + CustomerPortalSessionMock, +} from '../mocks/resources/customer-portal-sessions.mock.js'; + +describe('CustomerPortalSessionsResource', () => { + test('should create a new customer portal session', async () => { + const customerId = CustomerPortalSessionMock.customer_id; + const subscriptionIds = ['sub_123']; + const paddleInstance = getPaddleTestClient(); + paddleInstance.post = jest.fn().mockResolvedValue(CustomerPortalSessionMockResponse); + + const customerPortalSessionsResource = new CustomerPortalSessionsResource(paddleInstance); + const createdCustomerPortalSession = await customerPortalSessionsResource.create(customerId, subscriptionIds); + + expect(paddleInstance.post).toBeCalledWith(`/customers/${customerId}/portal-sessions`, { + subscriptionIds, + }); + expect(createdCustomerPortalSession).toBeDefined(); + expect(createdCustomerPortalSession.id).toBeDefined(); + expect(createdCustomerPortalSession.customerId).toBe(customerId); + expect(createdCustomerPortalSession.urls.subscriptions[0].id).toBe(subscriptionIds[0]); + }); +}); diff --git a/src/entities/customer-portal-session/customer-portal-session.ts b/src/entities/customer-portal-session/customer-portal-session.ts new file mode 100644 index 0000000..5cae8ff --- /dev/null +++ b/src/entities/customer-portal-session/customer-portal-session.ts @@ -0,0 +1,22 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type ICustomerPortalSessionResponse } from '../../types/index.js'; +import { Urls } from './urls.js'; + +export class CustomerPortalSession { + public readonly id: string; + public readonly customerId: string | null; + public readonly urls: Urls; + public readonly createdAt: string; + + constructor(customerPortalSessionResponse: ICustomerPortalSessionResponse) { + this.id = customerPortalSessionResponse.id; + this.customerId = customerPortalSessionResponse.customer_id; + this.urls = new Urls(customerPortalSessionResponse.urls); + this.createdAt = customerPortalSessionResponse.created_at; + } +} diff --git a/src/entities/customer-portal-session/customer-portal-subscription-url.ts b/src/entities/customer-portal-session/customer-portal-subscription-url.ts new file mode 100644 index 0000000..3659768 --- /dev/null +++ b/src/entities/customer-portal-session/customer-portal-subscription-url.ts @@ -0,0 +1,19 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type ICustomerPortalSubscriptionUrl } from '../../types/index.js'; + +export class CustomerPortalSubscriptionUrl { + public readonly id: string; + public readonly cancelSubscription: string; + public readonly updateSubscriptionPaymentMethod: string; + + constructor(customerPortalSubscriptionUrlResponse: ICustomerPortalSubscriptionUrl) { + this.id = customerPortalSubscriptionUrlResponse.id; + this.cancelSubscription = customerPortalSubscriptionUrlResponse.cancel_subscription; + this.updateSubscriptionPaymentMethod = customerPortalSubscriptionUrlResponse.update_subscription_payment_method; + } +} diff --git a/src/entities/customer-portal-session/general.ts b/src/entities/customer-portal-session/general.ts new file mode 100644 index 0000000..086f717 --- /dev/null +++ b/src/entities/customer-portal-session/general.ts @@ -0,0 +1,15 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type IGeneralResponse } from '../../types/index.js'; + +export class General { + public readonly overview: string; + + constructor(generalResponse: IGeneralResponse) { + this.overview = generalResponse.overview; + } +} diff --git a/src/entities/customer-portal-session/index.ts b/src/entities/customer-portal-session/index.ts new file mode 100644 index 0000000..127cbc2 --- /dev/null +++ b/src/entities/customer-portal-session/index.ts @@ -0,0 +1,8 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export * from './customer-portal-subscription-url.js'; +export * from './customer-portal-session.js'; diff --git a/src/entities/customer-portal-session/urls.ts b/src/entities/customer-portal-session/urls.ts new file mode 100644 index 0000000..ec1a43c --- /dev/null +++ b/src/entities/customer-portal-session/urls.ts @@ -0,0 +1,21 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type IUrlsResponse } from '../../types/index.js'; +import { General } from './general.js'; +import { CustomerPortalSubscriptionUrl } from './customer-portal-subscription-url.js'; + +export class Urls { + public readonly general: General; + public readonly subscriptions: CustomerPortalSubscriptionUrl[]; + + constructor(urlsResponse: IUrlsResponse) { + this.general = new General(urlsResponse.general); + this.subscriptions = urlsResponse.subscriptions?.map( + (subscription) => new CustomerPortalSubscriptionUrl(subscription) ?? [], + ); + } +} diff --git a/src/entities/index.ts b/src/entities/index.ts index f1d7b2c..676c926 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -10,6 +10,7 @@ export * from './price/index.js'; export * from './transaction/index.js'; export * from './adjustment/index.js'; export * from './customer/index.js'; +export * from './customer-portal-session/index.js'; export * from './business/index.js'; export * from './subscription/index.js'; export * from './address/index.js'; diff --git a/src/paddle.ts b/src/paddle.ts index de5bd08..6e5c0c1 100644 --- a/src/paddle.ts +++ b/src/paddle.ts @@ -5,6 +5,7 @@ import { AdjustmentsResource, BusinessesResource, CustomersResource, + CustomerPortalSessionsResource, DiscountsResource, EventTypesResource, NotificationSettingsResource, @@ -36,6 +37,7 @@ export class Paddle { public transactions: TransactionsResource; public adjustments: AdjustmentsResource; public customers: CustomersResource; + public customerPortalSessions: CustomerPortalSessionsResource; public addresses: AddressesResource; public businesses: BusinessesResource; public discounts: DiscountsResource; @@ -64,6 +66,7 @@ export class Paddle { this.transactions = new TransactionsResource(this.client); this.adjustments = new AdjustmentsResource(this.client); this.customers = new CustomersResource(this.client); + this.customerPortalSessions = new CustomerPortalSessionsResource(this.client); this.addresses = new AddressesResource(this.client); this.businesses = new BusinessesResource(this.client); this.discounts = new DiscountsResource(this.client); diff --git a/src/resources/customer-portal-sessions/index.ts b/src/resources/customer-portal-sessions/index.ts new file mode 100644 index 0000000..12d802c --- /dev/null +++ b/src/resources/customer-portal-sessions/index.ts @@ -0,0 +1,34 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { CustomerPortalSession } from '../../entities/index.js'; +import { type ICustomerPortalSessionResponse } from '../../types/index.js'; +import { type ErrorResponse, type Response } from '../../internal'; +import { BaseResource, PathParameters } from '../../internal/base'; +import { type CreateCustomerPortalSessionRequestBody } from './operations/index.js'; + +const CustomerPortalSessionPaths = { + create: '/customers/{customer_id}/portal-sessions', +} as const; + +export class CustomerPortalSessionsResource extends BaseResource { + public async create(customerId: string, subscriptionIds: string[]): Promise { + const urlWithPathParams = new PathParameters(CustomerPortalSessionPaths.create, { + customer_id: customerId, + }).deriveUrl(); + + const response = await this.client.post< + CreateCustomerPortalSessionRequestBody, + Response | ErrorResponse + >(urlWithPathParams, { + subscriptionIds, + }); + + const data = this.handleResponse(response); + + return new CustomerPortalSession(data); + } +} diff --git a/src/resources/customer-portal-sessions/operations/create-customer-portal-session-request-object.ts b/src/resources/customer-portal-sessions/operations/create-customer-portal-session-request-object.ts new file mode 100644 index 0000000..1c4ee20 --- /dev/null +++ b/src/resources/customer-portal-sessions/operations/create-customer-portal-session-request-object.ts @@ -0,0 +1,9 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export interface CreateCustomerPortalSessionRequestBody { + subscriptionIds: string[]; +} diff --git a/src/resources/customer-portal-sessions/operations/index.ts b/src/resources/customer-portal-sessions/operations/index.ts new file mode 100644 index 0000000..ad0ac2c --- /dev/null +++ b/src/resources/customer-portal-sessions/operations/index.ts @@ -0,0 +1,7 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export * from './create-customer-portal-session-request-object.js'; diff --git a/src/resources/index.ts b/src/resources/index.ts index afcc1dc..b35cce4 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -8,6 +8,7 @@ export * from './addresses/index.js'; export * from './adjustments/index.js'; export * from './businesses/index.js'; export * from './customers/index.js'; +export * from './customer-portal-sessions/index.js'; export * from './discounts/index.js'; export * from './prices/index.js'; export * from './products/index.js'; diff --git a/src/types/customer-portal-session/customer-portal-session-response.ts b/src/types/customer-portal-session/customer-portal-session-response.ts new file mode 100644 index 0000000..1473af7 --- /dev/null +++ b/src/types/customer-portal-session/customer-portal-session-response.ts @@ -0,0 +1,14 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type IUrlsResponse } from './urls.js'; + +export interface ICustomerPortalSessionResponse { + id: string; + customer_id: string; + urls: IUrlsResponse; + created_at: string; +} diff --git a/src/types/customer-portal-session/customer-portal-subscription-url.ts b/src/types/customer-portal-session/customer-portal-subscription-url.ts new file mode 100644 index 0000000..c7eb374 --- /dev/null +++ b/src/types/customer-portal-session/customer-portal-subscription-url.ts @@ -0,0 +1,11 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export interface ICustomerPortalSubscriptionUrl { + id: string; + cancel_subscription: string; + update_subscription_payment_method: string; +} diff --git a/src/types/customer-portal-session/general.ts b/src/types/customer-portal-session/general.ts new file mode 100644 index 0000000..942dcff --- /dev/null +++ b/src/types/customer-portal-session/general.ts @@ -0,0 +1,9 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export interface IGeneralResponse { + overview: string; +} diff --git a/src/types/customer-portal-session/index.ts b/src/types/customer-portal-session/index.ts new file mode 100644 index 0000000..adfa2a2 --- /dev/null +++ b/src/types/customer-portal-session/index.ts @@ -0,0 +1,10 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +export * from './general.js'; +export * from './customer-portal-subscription-url.js'; +export * from './urls.js'; +export * from './customer-portal-session-response.js'; diff --git a/src/types/customer-portal-session/urls.ts b/src/types/customer-portal-session/urls.ts new file mode 100644 index 0000000..e02997c --- /dev/null +++ b/src/types/customer-portal-session/urls.ts @@ -0,0 +1,13 @@ +/** + * ! Autogenerated code ! + * Do not make changes to this file. + * Changes may be overwritten as part of auto-generation. + */ + +import { type IGeneralResponse } from './general.js'; +import { type ICustomerPortalSubscriptionUrl } from './customer-portal-subscription-url.js'; + +export interface IUrlsResponse { + general: IGeneralResponse; + subscriptions: ICustomerPortalSubscriptionUrl[]; +} diff --git a/src/types/index.ts b/src/types/index.ts index a3f6d8c..3c90a6d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,6 +10,7 @@ export * from './price/index.js'; export * from './transaction/index.js'; export * from './adjustment/index.js'; export * from './customer/index.js'; +export * from './customer-portal-session/index.js'; export * from './business/index.js'; export * from './subscription/index.js'; export * from './address/index.js';