Skip to content

Commit

Permalink
feat(stripe): add ability to use custom decorators on controller
Browse files Browse the repository at this point in the history
* feat: add ability to use custom decorators on StripeWebhookController
* test: add a test for the decorator metadata to ensure it is set
  • Loading branch information
jmcdo29 authored May 17, 2021
1 parent ad7cd39 commit ebe8e72
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
17 changes: 16 additions & 1 deletion packages/stripe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Failure to give Stripe access to the raw body will result in nasty runtime error

### Decorate Methods For Processing Webhook Events

Exposing provider/service methods to be used for processing Stripe events is easy! Simply use the provided decorator and indiciate the event type that the handler should receive.
Exposing provider/service methods to be used for processing Stripe events is easy! Simply use the provided decorator and indicate the event type that the handler should receive.

[Review the Stripe documentation](https://stripe.com/docs/api/events/types) for more information about the types of events available.

Expand All @@ -121,6 +121,20 @@ class PaymentCreatedService {
}
```

### Webhook Controller Decorators

You can also pass any class decorator to the `decorators` property of the `webhookConfig` object as a part of the module configuration. This could be used in situations like when using the `@nestjs/throttler` package and needing to apply the `@ThrottlerSkip()` decorator, or when you have a global guard but need to skip routes with certain metadata.

````typescript
StripeModule.forRoot(StripeModule, {
apiKey: '123',
webhookConfig: {
stripeWebhookSecret: 'super-secret',
decorators: [ThrottlerSkip()],
},
}),
```

### Configure Webhooks in the Stripe Dashboard

Follow the instructions from the [Stripe Documentation](https://stripe.com/docs/webhooks) for remaining integration steps such as testing your integration with the CLI before you go live and properly configuring the endpoint from the Stripe dashboard so that the correct events are sent to your NestJS app.
Expand All @@ -132,3 +146,4 @@ Contributions welcome! Read the [contribution guidelines](../../CONTRIBUTING.md)
## License

[MIT License](../../LICENSE)
````
7 changes: 7 additions & 0 deletions packages/stripe/src/stripe.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export interface StripeModuleConfig extends Partial<Stripe.StripeConfig> {
*/
controllerPrefix?: string;

/**
* Any metadata specific decorators you want to apply to the webhook handling controller.
*
* Note: these decorators must only set metadata that will be read at request time. Decorators like Nest's `@UsePipes()` or `@UseInterceptors()` wll not work, due to the time at which Nest reads the metadata for those, but something that uses `SetMetadata` will be fine, because that metadata is read at request time.
*/
decorators?: ClassDecorator[];

/**
* Logging configuration
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/stripe/src/stripe.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class StripeModule
controllerPrefix,
StripeWebhookController
);
config.webhookConfig?.decorators?.forEach((deco) => {
deco(StripeWebhookController);
});
},
inject: [STRIPE_MODULE_CONFIG_TOKEN],
},
Expand Down
21 changes: 20 additions & 1 deletion packages/stripe/src/tests/stripe.module.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { INestApplication, Injectable } from '@nestjs/common';
import { INestApplication, Injectable, SetMetadata } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import Stripe from 'stripe';
import { InjectStripeClient } from '../stripe.decorators';
import { StripeWebhookController } from '../stripe.webhook.controller';
import { StripeModule } from './../stripe.module';

const testReceiveStripeFn = jest.fn();

const TestDecorator = () => SetMetadata('TEST:METADATA', 'metadata');

@Injectable()
class TestService {
constructor(@InjectStripeClient() private readonly stripeClient: Stripe) {
Expand Down Expand Up @@ -36,4 +39,20 @@ describe('Stripe Module', () => {
const client = testReceiveStripeFn.mock.calls[0][0];
expect(client).toBeInstanceOf(Stripe);
});
it('should apply the decorator to the controller', async () => {
await Test.createTestingModule({
imports: [
StripeModule.forRoot(StripeModule, {
apiKey: '123',
webhookConfig: {
stripeWebhookSecret: 'super-secret',
decorators: [TestDecorator()],
},
}),
],
}).compile();
expect(Reflect.getMetadata('TEST:METADATA', StripeWebhookController)).toBe(
'metadata'
);
});
});

0 comments on commit ebe8e72

Please sign in to comment.