Skip to content

Commit

Permalink
feat!(stripe, hasura): named execution contexts
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Switches to using named contexts for stripe and hasura which might have an impact on how interceptors and other NestJS enhancers work with methods that are decorated with the corresponding library handlers
  • Loading branch information
sgarner authored Jul 16, 2022
1 parent 2cc62bd commit 132c6c5
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 13 deletions.
24 changes: 24 additions & 0 deletions packages/hasura/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Leverage NestJS to make incorporating business logic and event processing easier
- [Registering Scheduled Event Handlers](#registering-scheduled-event-handlers)
- [Retry Configuration](#retry-configuration)
- [Configuring Hasura Environment Variables](#configuring-hasura-environment-variables)
- [Usage with Interceptors, Guards and Filters](#usage-with-interceptors-guards-and-filters)
- [Related Hasura Documentation](#related-hasura-documentation)
- [Concepts](#concepts)
- [Tutorials](#tutorials)
Expand Down Expand Up @@ -170,6 +171,29 @@ You should provide ENV variables to your Hasura instance that map the webhook en
In the examples above, `HASURA_NESTJS_WEBHOOK_SECRET_HEADER_VALUE` and `NESTJS_EVENT_WEBHOOK_ENDPOINT` were used. The webhook endpoint should point to the automatically scaffolded events endpoint eg:
`https://my-nest-app.com/api/hasura/events`

#### Usage with Interceptors, Guards and Filters

This library is built using an underlying NestJS concept called `External Contexts` which allows for methods to be included in the NestJS lifecycle. This means that Guards, Interceptors and Filters (collectively known as "enhancers") can be used in conjunction with Hasura event handlers. However, this can have unwanted/unintended consequences if you are using _Global_ enhancers in your application as these will also apply to all Hasura event handlers. If you were previously expecting all contexts to be regular HTTP contexts, you may need to add conditional logic to prevent your enhancers from applying to Hasura event handlers.

You can identify Hasura event contexts by their context type, `'hasura_event'`:

```typescript
@Injectable()
class ExampleInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler<any>) {
const contextType = context.getType<'http' | 'hasura_event'>();

// Do nothing if this is a Hasura event
if (contextType === 'hasura_event') {
return next.handle();
}

// Execute custom interceptor logic for HTTP request/response
return next.handle();
}
}
```

### Related Hasura Documentation

#### Concepts
Expand Down
8 changes: 7 additions & 1 deletion packages/hasura/src/hasura.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ export class HasuraModule
handler: this.externalContextCreator.create(
discoveredMethod.parentClass.instance,
discoveredMethod.handler,
discoveredMethod.methodName
discoveredMethod.methodName,
undefined, // metadataKey
undefined, // paramsFactory
undefined, // contextId
undefined, // inquirerId
undefined, // options
'hasura_event' // contextType
),
};
});
Expand Down
31 changes: 26 additions & 5 deletions packages/rabbitmq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- [Usage](#usage)
- [Install](#install)
- [Module Initialization](#module-initialization)
- [Usage with Interceptors](#usage-with-interceptors)
- [Usage with Interceptors, Guards and Filters](#usage-with-interceptors-guards-and-filters)
- [Usage with Controllers](#usage-with-controllers)
- [Receiving Messages](#receiving-messages)
- [Exposing RPC Handlers](#exposing-rpc-handlers)
Expand Down Expand Up @@ -134,18 +134,39 @@ import { MessagingService } from './messaging/messaging.service';
export class RabbitExampleModule {}
```

## Usage with Interceptors
## Usage with Interceptors, Guards and Filters

This library is built using an underlying NestJS concept called `External Contexts` which allows for methods to be included in the NestJS lifecycle. This means that Guards and Interceptors can be used in conjunction with RabbitMQ message handlers. However, this can have unwanted/unintended consequences if you are using Global intereceptors in your application as these will also apply to all RabbitMQ message handlers. As a workaround, there is a utiltity function available called `isRabbitContext` which you can use inside of Interceptors to do conditional logic.
This library is built using an underlying NestJS concept called `External Contexts` which allows for methods to be included in the NestJS lifecycle. This means that Guards, Interceptors and Filters (collectively known as "enhancers") can be used in conjunction with RabbitMQ message handlers. However, this can have unwanted/unintended consequences if you are using _Global_ enhancers in your application as these will also apply to all RabbitMQ message handlers. If you were previously expecting all contexts to be HTTP contexts, you may need to add conditional logic to prevent your enhancers from applying to RabbitMQ message handlers.

You can identify RabbitMQ contexts by their context type, `'rmq'`:

```typescript
@Injectable()
class ExampleInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler<any>) {
const contextType = context.getType<'http' | 'rmq'>();

// Do nothing if this is a RabbitMQ event
if (contextType === 'rmq') {
return next.handle();
}

// Execute custom interceptor logic for HTTP request/response
return next.handle();
}
}
```

There is also a utility function available called `isRabbitContext` which provides an alternative way to identify RabbitMQ contexts:

```typescript
import { isRabbitContext } from '@golevelup/nestjs-rabbitmq';

@Injectable()
class ExampleInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler<any>) {
const shouldSkip = isRabbitContext(context);
if (shouldSkip) {
// Do nothing if this is a RabbitMQ event
if (isRabbitContext(context)) {
return next.handle();
}

Expand Down
8 changes: 4 additions & 4 deletions packages/rabbitmq/src/rabbitmq.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ export class RabbitMQModule
discoveredMethod.methodName,
RABBIT_ARGS_METADATA,
this.rpcParamsFactory,
undefined,
undefined,
undefined,
'rmq'
undefined, // contextId
undefined, // inquirerId
undefined, // options
'rmq' // contextType
);

const { exchange, routingKey, queue, queueOptions } = config;
Expand Down
29 changes: 27 additions & 2 deletions packages/stripe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ Interacting with the Stripe API or consuming Stripe webhooks in your NestJS appl
#### NPM

- Install the package along with the stripe peer dependency
`npm install --save @golevelup/nestjs-stripe stripe`

`npm install --save @golevelup/nestjs-stripe stripe`

#### YARN

- Install the package using yarn with the stripe peer dependency
`yarn add @golevelup/nestjs-stripe stripe`

`yarn add @golevelup/nestjs-stripe stripe`

### Import

Expand Down Expand Up @@ -131,6 +133,29 @@ StripeModule.forRoot(StripeModule, {
}),
```

### Usage with Interceptors, Guards and Filters

This library is built using an underlying NestJS concept called `External Contexts` which allows for methods to be included in the NestJS lifecycle. This means that Guards, Interceptors and Filters (collectively known as "enhancers") can be used in conjunction with Stripe webhook handlers. However, this can have unwanted/unintended consequences if you are using _Global_ enhancers in your application as these will also apply to all Stripe webhook handlers. If you were previously expecting all contexts to be regular HTTP contexts, you may need to add conditional logic to prevent your enhancers from applying to Stripe webhook handlers.

You can identify Stripe webhook contexts by their context type, `'stripe_webhook'`:

```typescript
@Injectable()
class ExampleInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler<any>) {
const contextType = context.getType<'http' | 'stripe_webhook'>();

// Do nothing if this is a Stripe webhook event
if (contextType === 'stripe_webhook') {
return next.handle();
}

// Execute custom interceptor logic for HTTP request/response
return next.handle();
}
}
```

### 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 Down
8 changes: 7 additions & 1 deletion packages/stripe/src/stripe.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ export class StripeModule
handler: this.externalContextCreator.create(
discoveredMethod.parentClass.instance,
discoveredMethod.handler,
discoveredMethod.methodName
discoveredMethod.methodName,
undefined, // metadataKey
undefined, // paramsFactory
undefined, // contextId
undefined, // inquirerId
undefined, // options
'stripe_webhook' // contextType
),
}));
})
Expand Down

0 comments on commit 132c6c5

Please sign in to comment.