Skip to content

Add SubscriptionEvent wrapping/disable/enable and Invoice updateStatus/disable [PIP-311]#129

Open
wscourge wants to merge 5 commits intomainfrom
wiktor/pip-311-node-sdk-feature-updates
Open

Add SubscriptionEvent wrapping/disable/enable and Invoice updateStatus/disable [PIP-311]#129
wscourge wants to merge 5 commits intomainfrom
wiktor/pip-311-node-sdk-feature-updates

Conversation

@wscourge
Copy link
Contributor

@wscourge wscourge commented Mar 17, 2026

Summary

Closes PIP-311

  • Add auto-wrapping of flat params into subscription_event envelope for create/update/delete, with backward compatibility for already-enveloped inputs
  • Add SubscriptionEvent.disable / SubscriptionEvent.enable endpoints
  • Fix bug where passing undefined callback to _method() blocked request body from being sent
  • Add Invoice.updateStatus and Invoice.disable endpoints
  • Expand test coverage: body-wrapping verification, callback style, error handling, and Account include query param

Backwards compatibility review

All changes are backwards-compatible. No existing public API signatures or behaviours are broken.

Additive (zero risk)

New method Notes
Invoice.updateStatus(config, uuid, body[, cb]) New — PATCH /v1/invoices/:uuid
Invoice.disable(config, uuid[, body][, cb]) New — PATCH /v1/invoices/:uuid/disable
SubscriptionEvent.disable(config, id) New — PATCH /v1/subscription_events/:id/disable
SubscriptionEvent.enable(config, id) New — PATCH /v1/subscription_events/:id/enable

Behaviour changes (safe)

Change Detail Why it's safe
SubscriptionEvent.create now accepts flat params wrapParams auto-wraps {foo: 1}{subscription_event: {foo: 1}} Callers already passing the envelope are detected (data.subscription_event truthy) and not double-wrapped. Flat-param callers were previously sending the wrong shape to the API — this is a bug fix.
SubscriptionEvent.updateWithParams / deleteWithParams same wrapping Identical logic to above Same reasoning — envelope callers unaffected, flat callers get fixed.
Resource._method filters trailing undefined args Array.from(arguments).splice(1).filter(a => a !== undefined) On main, fn(config, data, undefined) would pop undefined as cb, then pop data as the next candidate — silently losing the request body. After the fix, undefined is stripped and data is correctly identified. No legitimate call path passes undefined intentionally. All 221 tests pass.

Not changed

  • SubscriptionEvent.all — untouched
  • Invoice.create / Invoice.all / Invoice.retrieve / Invoice.destroy / Invoice.destroy_all — untouched
  • All other SDK resources — untouched
  • Callback and promise interfaces — both supported, no signature changes

Test plan

  • All 221 tests pass (npm test)
  • Verified envelope wrapping via nock body capture
  • Verified backward compatibility (pre-wrapped params not double-wrapped)
  • Verified error responses return correct status codes
  • Rejection guards on all error tests (no silent passes)

Manual testing instructions

Prerequisites: Install the branch locally:

cd chartmogul-node
git checkout wiktor/pip-311-node-sdk-feature-updates
npm install
node

Then in the Node REPL:

const ChartMogul = require('./lib/chartmogul');
const config = new ChartMogul.Config('YOUR_API_KEY');

Use a staging/test account with existing subscription events and invoices.


1. SubscriptionEvent — flat params auto-wrapping (create)

Previously you had to wrap params in { subscription_event: { ... } }. Now flat params work too.

// Flat params (NEW) — should auto-wrap into envelope
const res = await ChartMogul.SubscriptionEvent.create(config, {
  customer_external_id: 'cus_test_001',
  data_source_uuid: 'ds_XXXX',
  event_type: 'subscription_start_scheduled',
  event_date: '2026-03-01T00:00:00Z',
  effective_date: '2026-04-01T00:00:00Z',
  external_id: 'evt_test_flat_' + Date.now(),
  subscription_external_id: 'sub_test_001'
});
console.log(res); // Should succeed and return { subscription_event: { id: ..., ... } }
// Envelope params (BACKWARD COMPAT) — should still work, no double-wrapping
const res2 = await ChartMogul.SubscriptionEvent.create(config, {
  subscription_event: {
    customer_external_id: 'cus_test_001',
    data_source_uuid: 'ds_XXXX',
    event_type: 'subscription_start_scheduled',
    event_date: '2026-03-01T00:00:00Z',
    effective_date: '2026-04-01T00:00:00Z',
    external_id: 'evt_test_envelope_' + Date.now(),
    subscription_external_id: 'sub_test_001'
  }
});
console.log(res2); // Should succeed identically

✅ Expected: Both calls succeed and return the created subscription event.


2. SubscriptionEvent — flat params auto-wrapping (update)

// Update using flat params with id
const updated = await ChartMogul.SubscriptionEvent.updateWithParams(config, {
  id: <EVENT_ID_FROM_STEP_1>,
  plan_external_id: 'plan_gold_monthly'
});
console.log(updated); // Should show updated plan_external_id
// Update using flat params with external_id + data_source_uuid
const updated2 = await ChartMogul.SubscriptionEvent.updateWithParams(config, {
  external_id: 'evt_test_flat_XXXX',
  data_source_uuid: 'ds_XXXX',
  plan_external_id: 'plan_silver_monthly'
});
console.log(updated2);

✅ Expected: Both calls succeed and return the updated subscription event.


3. SubscriptionEvent — flat params auto-wrapping (delete)

// Delete using flat params with id
await ChartMogul.SubscriptionEvent.deleteWithParams(config, {
  id: <EVENT_ID>
});
// Should return empty object {}
// Delete using flat params with external_id + data_source_uuid
await ChartMogul.SubscriptionEvent.deleteWithParams(config, {
  external_id: 'evt_test_envelope_XXXX',
  data_source_uuid: 'ds_XXXX'
});
// Should return empty object {}

✅ Expected: Both calls succeed with empty response {}.


4. SubscriptionEvent — disable / enable

// First create an event to work with (or use an existing event ID)
const event = await ChartMogul.SubscriptionEvent.create(config, {
  customer_external_id: 'cus_test_001',
  data_source_uuid: 'ds_XXXX',
  event_type: 'subscription_start_scheduled',
  event_date: '2026-03-01T00:00:00Z',
  effective_date: '2026-04-01T00:00:00Z',
  external_id: 'evt_disable_test_' + Date.now(),
  subscription_external_id: 'sub_test_001'
});
const eventId = event.subscription_event.id;

// Disable
const disabled = await ChartMogul.SubscriptionEvent.disable(config, eventId);
console.log(disabled); // subscription_event.disabled should be true

// Enable
const enabled = await ChartMogul.SubscriptionEvent.enable(config, eventId);
console.log(enabled); // subscription_event.disabled should be false
// Error case — nonexistent ID should return 404
try {
  await ChartMogul.SubscriptionEvent.disable(config, 999999999);
} catch (e) {
  console.log(e.status); // Should be 404
}

✅ Expected: Disable returns disabled: true, enable returns disabled: false, nonexistent ID returns 404.


5. Invoice — updateStatus

// Void an existing invoice (use a real invoice UUID from your test account)
const invoiceUuid = 'inv_XXXX';

const result = await ChartMogul.Invoice.updateStatus(config, invoiceUuid, {
  status: 'void'
});
console.log(result); // Should show status: 'void'
// Error case — invalid status transition should return 422
try {
  await ChartMogul.Invoice.updateStatus(config, invoiceUuid, {
    status: 'invalid_status'
  });
} catch (e) {
  console.log(e.status); // Should be 422
}

✅ Expected: Valid status update succeeds, invalid status returns 422.


6. Invoice — disable

const invoiceUuid = 'inv_XXXX';

const disabled = await ChartMogul.Invoice.disable(config, invoiceUuid);
console.log(disabled); // Should show disabled: true, disabled_at, disabled_by
// Error case — nonexistent invoice should return 404
try {
  await ChartMogul.Invoice.disable(config, 'inv_nonexistent');
} catch (e) {
  console.log(e.status); // Should be 404
}

✅ Expected: Disable succeeds with disabled: true + timestamps, nonexistent UUID returns 404.


🤖 Generated with Claude Code

wscourge and others added 3 commits March 17, 2026 17:05
…passing bug

- Wrap flat params in `subscription_event` envelope for create/update/delete
- Add disable/enable state toggling endpoints
- Fix bug where undefined callback arg blocked data extraction in _method()
- Add tests verifying body wrapping and error handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Invoice.updateStatus (PATCH /v1/invoices/:uuid)
- Add Invoice.disable (PATCH /v1/invoices/:uuid/disable)
- Add tests for happy path, callback style, body params, and error cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wscourge wscourge marked this pull request as ready for review March 17, 2026 16:16
@wscourge wscourge requested a review from Copilot March 17, 2026 16:17
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the ChartMogul Node SDK to support newer API behaviors for Subscription Events, Invoices, and Account retrieval by adding/adjusting client methods and expanding test coverage for these behaviors.

Changes:

  • Add support for flat (non-enveloped) SubscriptionEvent params by auto-wrapping into a subscription_event envelope, while keeping backward compatibility for already-enveloped inputs.
  • Add SubscriptionEvent.enable/disable and Invoice.updateStatus/disable SDK methods and corresponding tests.
  • Expand invoice/account test fixtures and assertions to cover new/optional response fields (e.g., error, id, and account include query param).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
lib/chartmogul/subscription_event.js Adds param-wrapping overrides for create/update/delete and adds enable/disable endpoints.
lib/chartmogul/invoice.js Adds PATCH helpers for invoice status updates and invoice disable endpoint.
test/chartmogul/subscription-event.js Updates tests to validate envelope wrapping + adds enable/disable tests and negative cases.
test/chartmogul/invoice.js Adds tests for invoice status updates/disable + extends fixtures with error fields.
test/chartmogul/account.js Adds assertions for id and supports include query param + negative case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

- Use hasOwnProperty check in wrapParams to handle falsy envelope values
- Use typeof callback === 'function' instead of truthy check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wscourge wscourge changed the title Wiktor/pip 311 node sdk feature updates Add SubscriptionEvent wrapping/disable/enable and Invoice updateStatus/disable [PIP-311] Mar 18, 2026
…arden tests

- Refactor SubscriptionEvent wrappers into shared wrapMethod helper
- Simplify wrapParams to use truthiness check instead of hasOwnProperty
- Fix root cause of undefined callback bug in Resource._method by filtering trailing undefined args
- Add rejection guards to all .catch-only error tests (invoice, subscription-event, account)
- Add callback-style test for SubscriptionEvent.create
- Add body capture assertion to Invoice.disable body params test
- Add clarifying comment on Invoice.updateStatus shared path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants