@stanlemon/server 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@stanlemon/server",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "A basic express web server setup.",
5
5
  "author": "Stan Lemon <stanlemon@users.noreply.github.com>",
6
6
  "license": "MIT",
7
7
  "engines": {
8
- "node": ">=17.0"
8
+ "node": ">=16.13.0"
9
9
  },
10
10
  "type": "module",
11
11
  "main": "./src/index.js",
@@ -13,21 +13,23 @@
13
13
  "scripts": {
14
14
  "start": "NODE_ENV=development nodemon ./app.js",
15
15
  "lint": "eslint --ext js,jsx,ts,tsx ./",
16
- "lint:format": "eslint --fix --ext js,jsx,ts,tsx ./"
16
+ "lint:format": "eslint --fix --ext js,jsx,ts,tsx ./",
17
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles",
18
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles --watch"
17
19
  },
18
20
  "dependencies": {
19
21
  "compression": "^1.7.4",
20
22
  "dotenv": "16.0.0",
21
- "express": "^4.17.2",
22
- "express-rate-limit": "^6.2.1",
23
+ "express": "^4.18.1",
24
+ "express-rate-limit": "^6.4.0",
23
25
  "helmet": "^5.0.2",
24
- "http-proxy-middleware": "^2.0.3",
26
+ "http-proxy-middleware": "^2.0.6",
25
27
  "joi": "^17.6.0",
26
28
  "lodash-es": "^4.17.21",
27
29
  "morgan": "^1.10.0"
28
30
  },
29
31
  "devDependencies": {
30
- "@stanlemon/eslint-config": "0.1.4",
31
- "nodemon": "^2.0.15"
32
+ "@stanlemon/eslint-config": "*",
33
+ "nodemon": "^2.0.16"
32
34
  }
33
- }
35
+ }
@@ -1,3 +1,4 @@
1
+ import { isPlainObject } from "lodash-es";
1
2
  import { formatInput } from "./index.js";
2
3
  import { formatOutput } from "./index.js";
3
4
 
@@ -5,16 +6,7 @@ export default asyncJsonHandler;
5
6
 
6
7
  export function asyncJsonHandler(fn) {
7
8
  return async (req, res /*, next */) => {
8
- const input = {
9
- // For POST & PUT requests we'll use the body
10
- // For everything else, it's query string parameters
11
- ...(req.method === "POST" || req.method === "PUT"
12
- ? formatInput(req.body)
13
- : req.query || {}),
14
- // Always make sure the request parameters override everything else
15
- // eg. a param for 'id' is not overridden by a query string 'id' or 'req.body.id'
16
- ...req.params,
17
- };
9
+ const input = buildInput(req);
18
10
 
19
11
  try {
20
12
  const output = await fn(input);
@@ -60,6 +52,26 @@ export function asyncJsonHandler(fn) {
60
52
  };
61
53
  }
62
54
 
55
+ function buildInput(req) {
56
+ const query = isPlainObject(req.query) ? req.query : {};
57
+ const params = isPlainObject(req.params) ? req.params : {};
58
+
59
+ if (req.method === "POST" || req.method === "PUT") {
60
+ const body = formatInput(req.body);
61
+
62
+ // This is a weird payload, don't try any of our append magic
63
+ if (!isPlainObject(body)) {
64
+ return body;
65
+ }
66
+
67
+ // Always make sure the request parameters override everything else
68
+ // eg. a param for 'id' is not overridden by a query string 'id' or 'req.body.id'
69
+ return { ...body, ...query, ...params };
70
+ }
71
+
72
+ return params;
73
+ }
74
+
63
75
  function formatError(ex) {
64
76
  return ex.message + (ex.details ? ": " + ex.details : "");
65
77
  }
@@ -0,0 +1,30 @@
1
+ import convertCase from "./convertCase";
2
+ import { snakeCase } from "lodash-es";
3
+
4
+ describe("convertCase()", () => {
5
+ const convert = (obj) => convertCase(obj, convert, snakeCase);
6
+
7
+ it("converts object keys", async () => {
8
+ expect(convert({ fooBar: true }, convertCase, snakeCase)).toEqual({
9
+ foo_bar: true,
10
+ });
11
+ });
12
+
13
+ it("converts an array of object keys", async () => {
14
+ expect(convert([{ fooBar: true }], convertCase, snakeCase)).toEqual([
15
+ {
16
+ foo_bar: true,
17
+ },
18
+ ]);
19
+ });
20
+
21
+ it("does not convert strings", async () => {
22
+ expect(convert("hereIsAString", convertCase, snakeCase)).toEqual(
23
+ "hereIsAString"
24
+ );
25
+ });
26
+
27
+ it("does not convert anything else", async () => {
28
+ expect(convert(true, convertCase, snakeCase)).toEqual(true);
29
+ });
30
+ });
@@ -14,7 +14,7 @@ export default function createAppServer(options) {
14
14
  const { port, webpack, start } = { ...DEFAULTS, ...options };
15
15
 
16
16
  const app = express();
17
- app.use(express.json());
17
+ app.use(express.json({ strict: false }));
18
18
 
19
19
  if (process.env.NODE_ENV !== "test") {
20
20
  app.use(morgan("combined"));
@@ -1,6 +1,9 @@
1
- import { snakeCase } from "lodash-es";
1
+ import { isArray, snakeCase } from "lodash-es";
2
2
  import convertCase from "./convertCase.js";
3
3
 
4
4
  export default function formatInput(obj) {
5
+ if (isArray(obj)) {
6
+ return obj.map((v) => formatInput(v));
7
+ }
5
8
  return convertCase(obj, formatInput, snakeCase);
6
9
  }
@@ -0,0 +1,31 @@
1
+ import formatInput from "./formatInput";
2
+
3
+ describe("formatInput()", () => {
4
+ it("formats object keys", async () => {
5
+ expect(formatInput({ fooBar: true })).toEqual({
6
+ foo_bar: true,
7
+ });
8
+ });
9
+
10
+ it("formats an array of object keys", async () => {
11
+ expect(formatInput([{ fooBar: true }])).toEqual([
12
+ {
13
+ foo_bar: true,
14
+ },
15
+ ]);
16
+ });
17
+
18
+ it("does not formats strings", async () => {
19
+ expect(formatInput("hereIsAString")).toEqual("hereIsAString");
20
+ });
21
+
22
+ it("does not formats anything else", async () => {
23
+ expect(formatInput(true)).toEqual(true);
24
+ });
25
+
26
+ it("does not change already formatted object keys", async () => {
27
+ expect(formatInput({ foo_bar: true })).toEqual({
28
+ foo_bar: true,
29
+ });
30
+ });
31
+ });
@@ -1,9 +1,16 @@
1
- import { camelCase, isObject, isArray, isDate, isEmpty, omit } from "lodash-es";
1
+ import {
2
+ camelCase,
3
+ isPlainObject,
4
+ isArray,
5
+ isDate,
6
+ isEmpty,
7
+ omit,
8
+ } from "lodash-es";
2
9
  import convertCase from "./convertCase.js";
3
10
 
4
11
  export default function formatOutput(o, omittedFields = []) {
5
12
  const obj =
6
- isObject(o) && !isEmpty(omittedFields) ? omit(o, omittedFields) : o;
13
+ isPlainObject(o) && !isEmpty(omittedFields) ? omit(o, omittedFields) : o;
7
14
  if (isDate(obj)) {
8
15
  return obj.toISOString();
9
16
  }
@@ -0,0 +1,31 @@
1
+ import formatOutput from "./formatOutput";
2
+
3
+ describe("formatOutput()", () => {
4
+ it("formats object keys", async () => {
5
+ expect(formatOutput({ foo_bar: true })).toEqual({
6
+ fooBar: true,
7
+ });
8
+ });
9
+
10
+ it("formats an array of object keys", async () => {
11
+ expect(formatOutput([{ foo_bar: true }])).toEqual([
12
+ {
13
+ fooBar: true,
14
+ },
15
+ ]);
16
+ });
17
+
18
+ it("does not formats strings", async () => {
19
+ expect(formatOutput("hereIsAString")).toEqual("hereIsAString");
20
+ });
21
+
22
+ it("does not formats anything else", async () => {
23
+ expect(formatOutput(true)).toEqual(true);
24
+ });
25
+
26
+ it("does not change already formatted object keys", async () => {
27
+ expect(formatOutput({ fooBar: true })).toEqual({
28
+ fooBar: true,
29
+ });
30
+ });
31
+ });