Skip to content

Commit

Permalink
Don't mutate error.type during minification (#2043)
Browse files Browse the repository at this point in the history
* accept hardcoded type in StripeError constructor

* Default to this.constructor.name when type is not provided

* Add test project

* Don't need eslint config
  • Loading branch information
anniel-stripe authored Mar 21, 2024
1 parent 800e45e commit 7aaf448
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ tags
coverage
.idea
testProjects/**/node_modules
testProjects/**/dist
testProjects/**/package-lock.json
testProjects/**/_worker.*
66 changes: 53 additions & 13 deletions src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export class StripeError extends Error {
readonly setup_intent?: any;
readonly source?: any;

constructor(raw: StripeRawError = {}) {
constructor(raw: StripeRawError = {}, type: string | null = null) {
super(raw.message);
this.type = this.constructor.name;
this.type = type || this.constructor.name;

this.raw = raw;
this.rawType = raw.type;
Expand Down Expand Up @@ -86,47 +86,75 @@ export class StripeError extends Error {
* CardError is raised when a user enters a card that can't be charged for
* some reason.
*/
export class StripeCardError extends StripeError {}
export class StripeCardError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeCardError');
}
}

/**
* InvalidRequestError is raised when a request is initiated with invalid
* parameters.
*/
export class StripeInvalidRequestError extends StripeError {}
export class StripeInvalidRequestError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeInvalidRequestError');
}
}

/**
* APIError is a generic error that may be raised in cases where none of the
* other named errors cover the problem. It could also be raised in the case
* that a new error has been introduced in the API, but this version of the
* Node.JS SDK doesn't know how to handle it.
*/
export class StripeAPIError extends StripeError {}
export class StripeAPIError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeAPIError');
}
}

/**
* AuthenticationError is raised when invalid credentials are used to connect
* to Stripe's servers.
*/
export class StripeAuthenticationError extends StripeError {}
export class StripeAuthenticationError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeAuthenticationError');
}
}

/**
* PermissionError is raised in cases where access was attempted on a resource
* that wasn't allowed.
*/
export class StripePermissionError extends StripeError {}
export class StripePermissionError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripePermissionError');
}
}

/**
* RateLimitError is raised in cases where an account is putting too much load
* on Stripe's API servers (usually by performing too many requests). Please
* back off on request rate.
*/
export class StripeRateLimitError extends StripeError {}
export class StripeRateLimitError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeRateLimitError');
}
}

/**
* StripeConnectionError is raised in the event that the SDK can't connect to
* Stripe's servers. That can be for a variety of different reasons from a
* downed network to a bad TLS certificate.
*/
export class StripeConnectionError extends StripeError {}
export class StripeConnectionError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeConnectionError');
}
}

/**
* SignatureVerificationError is raised when the signature verification for a
Expand All @@ -141,7 +169,7 @@ export class StripeSignatureVerificationError extends StripeError {
payload: string | Uint8Array,
raw: StripeRawError = {}
) {
super(raw);
super(raw, 'StripeSignatureVerificationError');
this.header = header;
this.payload = payload;
}
Expand All @@ -151,17 +179,29 @@ export class StripeSignatureVerificationError extends StripeError {
* IdempotencyError is raised in cases where an idempotency key was used
* improperly.
*/
export class StripeIdempotencyError extends StripeError {}
export class StripeIdempotencyError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeIdempotencyError');
}
}

/**
* InvalidGrantError is raised when a specified code doesn't exist, is
* expired, has been used, or doesn't belong to you; a refresh token doesn't
* exist, or doesn't belong to you; or if an API key's mode (live or test)
* doesn't match the mode of a code or refresh token.
*/
export class StripeInvalidGrantError extends StripeError {}
export class StripeInvalidGrantError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeInvalidGrantError');
}
}

/**
* Any other error from Stripe not specifically captured above
*/
export class StripeUnknownError extends StripeError {}
export class StripeUnknownError extends StripeError {
constructor(raw: StripeRawError = {}) {
super(raw, 'StripeUnknownError');
}
}
12 changes: 11 additions & 1 deletion test/Error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,17 @@ describe('Error', () => {
expect(e).to.have.property('statusCode', 400);
});

it('has subclasses which provide `.type` as their name', () => {
it('respects subclasses overriding `.type` in constructor', () => {
class Foo extends Error.StripeError {
constructor(raw: any) {
super(raw, 'Foo');
}
}
const err = new Foo({message: 'hi'});
expect(err).to.have.property('type', 'Foo');
});

it('defaults setting `.type` to subclass name', () => {
class Foo extends Error.StripeError {}
const err = new Foo({message: 'hi'});
expect(err).to.have.property('type', 'Foo');
Expand Down
16 changes: 16 additions & 0 deletions test/Integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ describe('Integration test', function() {
await runTestProject('mjs-ts');
});

describe('esbuild', () => {
it('should not change error.type when minified', async function() {
// Node supports ES Modules starting at v12
if (nodeVersion <= 12) {
this.skip();
}

await testExec(`
cd testProjects/esbuild && rm -rf node_modules && rm -rf dist
npm install &&
npm run build &&
npm run start
`);
});
});

const runTestCloudflareProject = (projectName: string): Promise<void> => {
if (process.versions.node < '16.13') {
console.log('Wrangler requires at least node.js v16.13.0, skipping test');
Expand Down
35 changes: 35 additions & 0 deletions testProjects/esbuild/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {Stripe} from 'stripe';
import assert from 'assert';

// API key is null to trigger an authentication error
const stripe = new Stripe(null, {
host: process.env.STRIPE_MOCK_HOST || 'localhost',
port: process.env.STRIPE_MOCK_PORT || 12111,
protocol: 'http',
});

try {
throw new stripe.errors.StripeUnknownError({
charge: 'foo',
unknown_prop: 'bar',
});
} catch (e) {
assert (e instanceof stripe.errors.StripeUnknownError);
assert (e.type === 'StripeUnknownError');
}

async function exampleFunction(args) {
try {
await stripe.paymentIntents.create(args);
} catch (e) {
assert (e instanceof stripe.errors.StripeAuthenticationError);
assert (e.type === 'StripeAuthenticationError');
}
}

exampleFunction({
// The required parameter currency is missing
amount: 2000,
confirm: true,
payment_method: 'pm_card_visa',
});
17 changes: 17 additions & 0 deletions testProjects/esbuild/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "mjs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"stripe": "file:../../"
},
"scripts": {
"clean": "rm -rf node_modules && rm -rf dist",
"build": "esbuild index.js --bundle --platform=node --minify --target=es2020 --outdir=dist",
"start": "node dist/index.js"
},
"devDependencies": {
"esbuild": "^0.20.2"
}
}

0 comments on commit 7aaf448

Please sign in to comment.