@@ -16,13 +16,14 @@ | |||||
"@types/node": "^18.14.1", | "@types/node": "^18.14.1", | ||||
"eslint": "^8.35.0", | "eslint": "^8.35.0", | ||||
"eslint-config-lxsmnsyc": "^0.5.0", | "eslint-config-lxsmnsyc": "^0.5.0", | ||||
"fastify": "^4.12.0", | |||||
"pridepack": "2.4.4", | "pridepack": "2.4.4", | ||||
"tslib": "^2.5.0", | "tslib": "^2.5.0", | ||||
"typescript": "^4.9.5", | "typescript": "^4.9.5", | ||||
"vitest": "^0.28.1" | "vitest": "^0.28.1" | ||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"fastify": "^4.12.0", | |||||
"@modal-sh/oatmeal-core": "file:../core", | |||||
"fastify-plugin": "^4.5.0" | "fastify-plugin": "^4.5.0" | ||||
}, | }, | ||||
"scripts": { | "scripts": { | ||||
@@ -1,5 +1,40 @@ | |||||
import { fastifyPlugin } from 'fastify-plugin'; | import { fastifyPlugin } from 'fastify-plugin'; | ||||
import * as oatmeal from '@modal-sh/oatmeal-core'; | |||||
import { FastifyReply, FastifyRequest} from 'fastify'; | |||||
const JSON_TYPES = [ | |||||
'application/json', | |||||
] as const | |||||
interface SendX { | |||||
(contentType?: string, data?: unknown): void; | |||||
} | |||||
const sendX: SendX = function sendX(this: FastifyReply, contentType?: string, data?: unknown) { | |||||
if (typeof contentType !== 'undefined' && typeof data !== 'undefined') { | |||||
const content = oatmeal.serialize(data, { type: contentType }) | |||||
// TODO add error handling when payload can't be serialized | |||||
this.type(contentType).send(content); | |||||
return; | |||||
} | |||||
this.send(); | |||||
} | |||||
export const fastifyOatmeal = fastifyPlugin(async (fastify) => { | export const fastifyOatmeal = fastifyPlugin(async (fastify) => { | ||||
oatmeal.AVAILABLE_TYPES | |||||
.filter((type) => !JSON_TYPES.includes(type as typeof JSON_TYPES[number])) | |||||
.forEach((type) => { | |||||
fastify.addContentTypeParser(type, { parseAs: 'buffer' }, async (_: FastifyRequest, body: Buffer) => { | |||||
return oatmeal.deserialize(body.toString('utf-8'), { type }); | |||||
// TODO add error handling when body can't be deserialized | |||||
}); | |||||
}); | |||||
fastify.decorateReply('sendX', sendX); | |||||
}); | }); | ||||
declare module 'fastify' { | |||||
interface FastifyReply { | |||||
sendX: SendX; | |||||
} | |||||
} |
@@ -0,0 +1,138 @@ | |||||
import {describe, it, expect, beforeEach, afterEach} from 'vitest'; | |||||
import {fastify, FastifyInstance} from 'fastify'; | |||||
import { fastifyOatmeal } from '../src'; | |||||
describe('oatmeal-fastify', () => { | |||||
let server: FastifyInstance; | |||||
beforeEach(() => { | |||||
server = fastify(); | |||||
server.route({ | |||||
url: '/', | |||||
method: 'POST', | |||||
handler: async (request, reply) => { | |||||
return reply.sendX(request.headers['accept'] as string, request.body); | |||||
}, | |||||
}); | |||||
}); | |||||
afterEach(async () => { | |||||
await server.close(); | |||||
}); | |||||
describe('deserialize', () => { | |||||
it('deserializes application/json', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
'Content-Type': 'application/json', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
it('deserializes text/json', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
'Content-Type': 'text/json', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
it('deserializes application/xml', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(`<root type="object"><hello type="string">world</hello></root>`) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
'Content-Type': 'application/xml', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
it('deserializes text/xml', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(`<root type="object"><hello type="string">world</hello></root>`) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
'Content-Type': 'text/xml', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
}); | |||||
describe('serialize', () => { | |||||
it('serializes application/json', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
'Content-Type': 'application/json', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
it('serializes text/json', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'text/json', | |||||
'Content-Type': 'application/json', | |||||
}); | |||||
expect(response.body).toBe(JSON.stringify({ hello: 'world' })); | |||||
}); | |||||
it('deserializes application/xml', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'application/xml', | |||||
'Content-Type': 'application/json', | |||||
}); | |||||
expect(response.body).toBe('<root type="object"><hello type="string">world</hello></root>'); | |||||
}); | |||||
it('deserializes application/xml', async () => { | |||||
await server.register(fastifyOatmeal); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.body(JSON.stringify({ hello: 'world' })) | |||||
.headers({ | |||||
'Accept': 'text/xml', | |||||
'Content-Type': 'application/json', | |||||
}); | |||||
expect(response.body).toBe('<root type="object"><hello type="string">world</hello></root>'); | |||||
}); | |||||
}); | |||||
}); |
@@ -1,15 +0,0 @@ | |||||
import { describe, it, expect } from 'vitest'; | |||||
import SERVER from '../src/server'; | |||||
import '../src/routes'; | |||||
describe('Example', () => { | |||||
it('should have the expected content', async () => { | |||||
const response = await SERVER | |||||
.inject() | |||||
.get('/') | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
}); | |||||
expect(response.statusCode).toBe(200); | |||||
}); | |||||
}); |
@@ -470,6 +470,11 @@ | |||||
resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.2.47.tgz#421bed6950958adf9b2aee8386aeac9d92e22045" | resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.2.47.tgz#421bed6950958adf9b2aee8386aeac9d92e22045" | ||||
integrity sha512-6/QvoKeooo3J/WL7i9yjfDtUkqOZW8K6aqdzcw+bz4YdgMBzBQVZU7vZmEdCGOcV5AlsBHZT38mqx/sTrnZMDQ== | integrity sha512-6/QvoKeooo3J/WL7i9yjfDtUkqOZW8K6aqdzcw+bz4YdgMBzBQVZU7vZmEdCGOcV5AlsBHZT38mqx/sTrnZMDQ== | ||||
"@modal-sh/oatmeal-core@file:../core": | |||||
version "0.0.0" | |||||
dependencies: | |||||
xml-js "^1.6.11" | |||||
"@next/eslint-plugin-next@^13.2.4": | "@next/eslint-plugin-next@^13.2.4": | ||||
version "13.2.4" | version "13.2.4" | ||||
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.2.4.tgz#3e124cd10ce24dab5d3448ce04104b4f1f4c6ca7" | resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.2.4.tgz#3e124cd10ce24dab5d3448ce04104b4f1f4c6ca7" | ||||
@@ -3206,6 +3211,11 @@ safe-stable-stringify@^2.3.1: | |||||
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" | resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" | ||||
integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== | integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== | ||||
sax@^1.2.4: | |||||
version "1.2.4" | |||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" | |||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== | |||||
secure-json-parse@^2.5.0: | secure-json-parse@^2.5.0: | ||||
version "2.7.0" | version "2.7.0" | ||||
resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" | resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" | ||||
@@ -3811,6 +3821,13 @@ xdg-basedir@^4.0.0: | |||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" | resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" | ||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== | integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== | ||||
xml-js@^1.6.11: | |||||
version "1.6.11" | |||||
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" | |||||
integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== | |||||
dependencies: | |||||
sax "^1.2.4" | |||||
xml-name-validator@^4.0.0: | xml-name-validator@^4.0.0: | ||||
version "4.0.0" | version "4.0.0" | ||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" | resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" | ||||