Skip to content

Commit

Permalink
Merge pull request #54 from bouwe77/rename-requestbodyinterceptor-to-…
Browse files Browse the repository at this point in the history
…requestinterceptor

rename requestBodyInterceptor to requestInterceptor
  • Loading branch information
bouwe77 authored May 9, 2024
2 parents 31a85c5 + dc73b30 commit 63d39e6
Show file tree
Hide file tree
Showing 17 changed files with 100 additions and 109 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Temba supports JSON only.

Request bodies sent with a `POST`, `PATCH`, and `PUT` requests are valid when the request body is either empty, or when it's valid formatted JSON. Adding a `Content-Type: application/json` header is required. If you send a request with invalid formatted JSON, a `400 Bad Request` response is returned.

Any valid formatted JSON is accepted and stored. If you want to validate or even change the JSON in the request bodies, check out [JSON Schema request body validation](#json-schema-request-body-validation) and the [`requestBodyInterceptor`](#request-body-validation-or-mutation).
Any valid formatted JSON is accepted and stored. If you want to validate or even change the JSON in the request bodies, check out [JSON Schema request body validation](#json-schema-request-body-validation) and the [`requestInterceptor`](#request-validation-or-mutation).

IDs are auto generated when creating resources. IDs in the JSON request body are always ignored.

Expand Down Expand Up @@ -256,13 +256,13 @@ If a request is not valid according to the schema, a `400 Bad Request` response

### Intercepting requests

In addition to (or instead of) validating the request using JSON Schema, you can also intercept the request body before it is persisted, using the `requestBodyInterceptor` setting.
In addition to (or instead of) validating the request using JSON Schema, you can also intercept the request before it is persisted, using the `requestInterceptor` setting.

It allows you to implement your own validation, or even change the request body.

```js
const config = {
requestBodyInterceptor: {
requestInterceptor: {
post: ({ resource, body }) => {
// Validate, or even change the request body
},
Expand All @@ -278,7 +278,9 @@ const config = {
const server = temba.create(config)
```

The `requestBodyInterceptor` is an object with a `post`, and/or `patch`, and/or `put` field, which contains the callback function you want Temba to call before the JSON is persisted.
> At the moment, only `POST`, `PATCH`, and `PUT` requests can be intercepted.
The `requestInterceptor` is an object with a `post`, and/or `patch`, and/or `put` field, which contains the callback function you want Temba to call before the JSON is persisted.

The callback function receives an object containing the `resource`, which for example is `movies` if you request `POST /movies`, and the `body`, which is the JSON object of the request body.

Expand All @@ -292,7 +294,7 @@ Example:

```js
const config = {
requestBodyInterceptor: {
requestInterceptor: {
post: ({ resource, body }) => {
// Do not allow Pokemons to be created: 400 Bad Request
if (resource === 'pokemons') return 'You are not allowed to create new Pokemons'
Expand Down Expand Up @@ -425,7 +427,7 @@ const config = {
customRouter: router,
delay: 500,
port: 4321,
requestBodyInterceptor: {
requestInterceptor: {
post: ({ resource, body }) => {
// Validate, or even change the request body
},
Expand Down Expand Up @@ -467,9 +469,9 @@ These are all the possible settings:
| `customRouter` | See [Custom router](#custom-router) | `null` |
| `delay` | The delay, in milliseconds, after processing the request before sending the response. | `0` |
| `port` | The port your Temba server listens on | `3000` |
| `requestBodyInterceptor` | See [Request body validation or mutation](#request-body-validation-or-mutation) | `noop` |
| `requestInterceptor` | See [Request validation or mutation](#request-validation-or-mutation) | `noop` |
| `resources` | See [Allowing specific resources only](#allowing-specific-resources-only) | `[]` |
| `responseBodyInterceptor` | See [Response body interception](#request-body-validation-or-mutation) | `noop` |
| `responseBodyInterceptor` | See [Response body interception](#response-body-interception) | `noop` |
| `returnNullFields` | Determines whether fields with a null value should be returned in responses. | `true` |
| `schema` | See [JSON Schema request body validation](#json-schema-request-body-validation) | `null` |
| `staticFolder` | See [Static assets](#static-assets) | `null` |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "temba",
"version": "0.25.0",
"version": "0.26.0",
"description": "Get a simple REST API with zero coding in less than 30 seconds (seriously).",
"type": "module",
"main": "dist/src/index.js",
Expand Down
32 changes: 16 additions & 16 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Router } from 'express'
import type { ConfiguredSchemas } from '../schema/types'
import type { RequestBodyInterceptor } from '../requestBodyInterceptor/types'
import type { RequestInterceptor } from '../requestInterceptor/types'
import type { ResponseBodyInterceptor } from '../responseBodyInterceptor/types'

export type Config = {
validateResources: boolean
resources: string[]
apiPrefix: string | null
cacheControl: string
requestBodyInterceptor: RequestBodyInterceptor | null
requestInterceptor: RequestInterceptor | null
responseBodyInterceptor: ResponseBodyInterceptor | null
staticFolder: string | null
connectionString: string | null
Expand All @@ -28,7 +28,7 @@ export type RouterConfig = Pick<
| 'resources'
| 'apiPrefix'
| 'cacheControl'
| 'requestBodyInterceptor'
| 'requestInterceptor'
| 'responseBodyInterceptor'
| 'returnNullFields'
>
Expand All @@ -40,7 +40,7 @@ export type UserConfig = {
connectionString?: string
cacheControl?: string
delay?: number
requestBodyInterceptor?: RequestBodyInterceptor
requestInterceptor?: RequestInterceptor
responseBodyInterceptor?: ResponseBodyInterceptor
customRouter?: Router
returnNullFields?: boolean
Expand All @@ -57,7 +57,7 @@ const defaultConfig: Config = {
connectionString: null,
cacheControl: 'no-store',
delay: 0,
requestBodyInterceptor: null,
requestInterceptor: null,
responseBodyInterceptor: null,
customRouter: null,
returnNullFields: true,
Expand Down Expand Up @@ -102,26 +102,26 @@ export const initConfig = (userConfig?: UserConfig): Config => {
config.delay = userConfig.delay
}

if (userConfig.requestBodyInterceptor) {
config.requestBodyInterceptor = config.requestBodyInterceptor || {}
if (userConfig.requestInterceptor) {
config.requestInterceptor = config.requestInterceptor || {}

if (
userConfig.requestBodyInterceptor.post &&
typeof userConfig.requestBodyInterceptor.post === 'function'
userConfig.requestInterceptor.post &&
typeof userConfig.requestInterceptor.post === 'function'
) {
config.requestBodyInterceptor.post = userConfig.requestBodyInterceptor.post
config.requestInterceptor.post = userConfig.requestInterceptor.post
}
if (
userConfig.requestBodyInterceptor.patch &&
typeof userConfig.requestBodyInterceptor.patch === 'function'
userConfig.requestInterceptor.patch &&
typeof userConfig.requestInterceptor.patch === 'function'
) {
config.requestBodyInterceptor.patch = userConfig.requestBodyInterceptor.patch
config.requestInterceptor.patch = userConfig.requestInterceptor.patch
}
if (
userConfig.requestBodyInterceptor.put &&
typeof userConfig.requestBodyInterceptor.put === 'function'
userConfig.requestInterceptor.put &&
typeof userConfig.requestInterceptor.put === 'function'
) {
config.requestBodyInterceptor.put = userConfig.requestBodyInterceptor.put
config.requestInterceptor.put = userConfig.requestInterceptor.put
}
}

Expand Down
12 changes: 0 additions & 12 deletions src/requestBodyInterceptor/types.ts

This file was deleted.

13 changes: 4 additions & 9 deletions src/requestHandlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const getRequestHandler = (
schemas: CompiledSchemas,
routerConfig: RouterConfig,
) => {
const { cacheControl, requestBodyInterceptor, responseBodyInterceptor, returnNullFields } =
const { cacheControl, requestInterceptor, responseBodyInterceptor, returnNullFields } =
routerConfig

const handleGet = createGetRoutes(
Expand All @@ -23,18 +23,13 @@ export const getRequestHandler = (
returnNullFields,
)

const handlePost = createPostRoutes(
queries,
requestBodyInterceptor,
returnNullFields,
schemas.post,
)
const handlePost = createPostRoutes(queries, requestInterceptor, returnNullFields, schemas.post)

const handlePut = createPutRoutes(queries, requestBodyInterceptor, returnNullFields, schemas.put)
const handlePut = createPutRoutes(queries, requestInterceptor, returnNullFields, schemas.put)

const handlePatch = createPatchRoutes(
queries,
requestBodyInterceptor,
requestInterceptor,
returnNullFields,
schemas.patch,
)
Expand Down
10 changes: 5 additions & 5 deletions src/requestHandlers/patch.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { interceptRequestBody } from '../requestBodyInterceptor/interceptRequestBody'
import { interceptRequest } from '../requestInterceptor/interceptRequest'
import { validate } from '../schema/validate'
import { removeNullFields } from './utils'
import type { ValidateFunctionPerResource } from '../schema/types'
import type { PatchRequest } from './types'
import type { Queries } from '../data/types'
import type { RequestBodyInterceptor } from '../requestBodyInterceptor/types'
import type { RequestInterceptor } from '../requestInterceptor/types'

export const createPatchRoutes = (
queries: Queries,
requestBodyInterceptor: RequestBodyInterceptor | null,
requestInterceptor: RequestInterceptor | null,
returnNullFields: boolean,
schemas: ValidateFunctionPerResource | null,
) => {
Expand All @@ -21,8 +21,8 @@ export const createPatchRoutes = (
return { status: 400, body: { message: validationResult.errorMessage } }
}

const body2 = requestBodyInterceptor?.patch
? interceptRequestBody(requestBodyInterceptor.patch, resource, body)
const body2 = requestInterceptor?.patch
? interceptRequest(requestInterceptor.patch, resource, body)
: body

if (typeof body2 === 'string') return { status: 400, body: { message: body2 } }
Expand Down
10 changes: 5 additions & 5 deletions src/requestHandlers/post.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { format } from 'url'
import { interceptRequestBody } from '../requestBodyInterceptor/interceptRequestBody'
import { interceptRequest } from '../requestInterceptor/interceptRequest'
import { removeNullFields } from './utils'
import { validate } from '../schema/validate'
import type { ValidateFunctionPerResource } from '../schema/types'
import type { PostRequest } from './types'
import type { ItemWithoutId, Queries } from '../data/types'
import type { RequestBodyInterceptor } from '../requestBodyInterceptor/types'
import type { RequestInterceptor } from '../requestInterceptor/types'

export const createPostRoutes = (
queries: Queries,
requestBodyInterceptor: RequestBodyInterceptor | null,
requestInterceptor: RequestInterceptor | null,
returnNullFields: boolean,
schemas: ValidateFunctionPerResource,
) => {
Expand All @@ -22,8 +22,8 @@ export const createPostRoutes = (
return { status: 400, body: { message: validationResult.errorMessage } }
}

const body2 = requestBodyInterceptor?.post
? interceptRequestBody(requestBodyInterceptor.post, resource, body)
const body2 = requestInterceptor?.post
? interceptRequest(requestInterceptor.post, resource, body)
: body

if (typeof body2 === 'string') return { status: 400, body: { message: body2 } }
Expand Down
10 changes: 5 additions & 5 deletions src/requestHandlers/put.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { interceptRequestBody } from '../requestBodyInterceptor/interceptRequestBody'
import { interceptRequest } from '../requestInterceptor/interceptRequest'
import { validate } from '../schema/validate'
import { removeNullFields } from './utils'
import type { ValidateFunctionPerResource } from '../schema/types'
import type { PutRequest } from './types'
import type { Queries } from '../data/types'
import type { RequestBodyInterceptor } from '../requestBodyInterceptor/types'
import type { RequestInterceptor } from '../requestInterceptor/types'

export const createPutRoutes = (
queries: Queries,
requestBodyInterceptor: RequestBodyInterceptor | null,
requestInterceptor: RequestInterceptor | null,
returnNullFields: boolean,
schemas: ValidateFunctionPerResource | null,
) => {
Expand All @@ -21,8 +21,8 @@ export const createPutRoutes = (
return { status: 400, body: { message: validationResult.errorMessage } }
}

const body2 = requestBodyInterceptor?.put
? interceptRequestBody(requestBodyInterceptor.put, resource, body)
const body2 = requestInterceptor?.put
? interceptRequest(requestInterceptor.put, resource, body)
: body

if (typeof body2 === 'string') return { status: 400, body: { message: body2 } }
Expand Down
2 changes: 1 addition & 1 deletion src/requestHandlers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type ErrorResponse = {
status: number
}

export interface TembaRequest {
export type TembaRequest = {
resource: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RequestBodyInterceptorCallback } from './types'
import type { RequestInterceptorCallback } from './types'

export const interceptRequestBody = (
intercept: RequestBodyInterceptorCallback,
export const interceptRequest = (
intercept: RequestInterceptorCallback,
resource: string,
body: unknown,
) => {
Expand Down
12 changes: 12 additions & 0 deletions src/requestInterceptor/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type InterceptedRequest = {
resource: string
body: unknown
}

export type RequestInterceptorCallback = (info: InterceptedRequest) => void | string | object

export type RequestInterceptor = {
post?: RequestInterceptorCallback
patch?: RequestInterceptorCallback
put?: RequestInterceptorCallback
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { describe, test, expect } from 'vitest'
import request from 'supertest'
import type { UserConfig } from '../../../src/config'
import createServer from '../createServer'
import type { RequestBodyInterceptor } from '../../../src/requestBodyInterceptor/types'
import type { RequestInterceptor } from '../../../src/requestInterceptor/types'

describe('requestBodyInterceptors that return a (new or changed) request body object', () => {
const requestBodyInterceptor: RequestBodyInterceptor = {
describe('requestInterceptors that return a (new or changed) request body object', () => {
const requestInterceptor: RequestInterceptor = {
post: ({ resource }) => {
if (resource === 'movies') return { title: 'The Matrix' }
},
Expand All @@ -17,9 +17,9 @@ describe('requestBodyInterceptors that return a (new or changed) request body ob
},
}

const tembaServer = createServer({ requestBodyInterceptor } satisfies UserConfig)
const tembaServer = createServer({ requestInterceptor } satisfies UserConfig)

test('POST with a requestBodyInterceptor that returns a request body', async () => {
test('POST with a requestInterceptor that returns a request body', async () => {
const resourceUrl = '/movies'

// Send a POST request.
Expand All @@ -39,7 +39,7 @@ describe('requestBodyInterceptors that return a (new or changed) request body ob
expect(getResponse.body.title).toEqual('The Matrix')
})

test('PUT with a requestBodyInterceptor that returns a request body', async () => {
test('PUT with a requestInterceptor that returns a request body', async () => {
const resourceUrl = '/pokemons'

// First create a resource, so we have an id to PUT to.
Expand All @@ -65,7 +65,7 @@ describe('requestBodyInterceptors that return a (new or changed) request body ob
expect(response.body.replaced).toEqual(true)
})

test('PATCH with a requestBodyInterceptor that returns a request body', async () => {
test('PATCH with a requestInterceptor that returns a request body', async () => {
const resourceUrl = '/pokemons'

// First create a resource, so we have an id to PUT to.
Expand Down
Loading

0 comments on commit 63d39e6

Please sign in to comment.