Browse Source

Implement fastify plugin

Fastify plugin just calls from core package.
master
TheoryOfNekomata 1 year ago
parent
commit
476a413ef1
7 changed files with 192 additions and 16 deletions
  1. +2
    -1
      package.json
  2. +35
    -0
      src/index.ts
  3. +0
    -0
      src/routes.ts
  4. +0
    -0
      src/server.ts
  5. +138
    -0
      test/index.test.ts
  6. +0
    -15
      test/index.test.tsx
  7. +17
    -0
      yarn.lock

+ 2
- 1
package.json View File

@@ -16,13 +16,14 @@
"@types/node": "^18.14.1",
"eslint": "^8.35.0",
"eslint-config-lxsmnsyc": "^0.5.0",
"fastify": "^4.12.0",
"pridepack": "2.4.4",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vitest": "^0.28.1"
},
"dependencies": {
"fastify": "^4.12.0",
"@modal-sh/oatmeal-core": "file:../core",
"fastify-plugin": "^4.5.0"
},
"scripts": {


+ 35
- 0
src/index.ts View File

@@ -1,5 +1,40 @@
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) => {
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
src/routes.ts View File


+ 0
- 0
src/server.ts View File


+ 138
- 0
test/index.test.ts View File

@@ -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>');
});
});
});

+ 0
- 15
test/index.test.tsx View File

@@ -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);
});
});

+ 17
- 0
yarn.lock View File

@@ -470,6 +470,11 @@
resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.2.47.tgz#421bed6950958adf9b2aee8386aeac9d92e22045"
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":
version "13.2.4"
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"
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:
version "2.7.0"
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"
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:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"


Loading…
Cancel
Save