-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf: rewrite using lua scripting when is possible (#16)
* docs: add server example * test: unify error assertions * refactor: better error handling * refactor: extract util
- Loading branch information
Showing
12 changed files
with
218 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
'use strict' | ||
|
||
const { styleText } = require('node:util') | ||
const { json } = require('http-body') | ||
const send = require('send-http') | ||
const Redis = require('ioredis') | ||
const http = require('http') | ||
|
||
const redis = new Redis() | ||
const openkey = require('../..')({ redis }) | ||
|
||
const server = http.createServer(async (req, res) => { | ||
try { | ||
console.log(`~> ${req.method} ${req.url} `) | ||
|
||
if (req.url === '/keys/create') { | ||
const options = await json(req) | ||
const result = await openkey.keys.create(options) | ||
return send(res, 201, result) | ||
} | ||
|
||
if (req.url.startsWith('/keys/key_')) { | ||
const key = req.url.split('/keys/')[1] | ||
const result = await openkey.keys.retrieve(key) | ||
return send(res, 200, result) | ||
} | ||
|
||
if (req.url === '/plans/create') { | ||
const options = await json(req) | ||
const result = await openkey.plans.create(options) | ||
return send(res, 201, result) | ||
} | ||
|
||
if (req.url.startsWith('/usage/')) { | ||
const keyValue = req.url.split('/usage/')[1] | ||
const { pending, ...usage } = await openkey.usage.increment(keyValue) | ||
const statusCode = usage.remaining > 0 ? 200 : 429 | ||
res.setHeader('X-Rate-Limit-Limit', usage.limit) | ||
res.setHeader('X-Rate-Limit-Remaining', usage.remaining) | ||
res.setHeader('X-Rate-Limit-Reset', usage.reset) | ||
return send(res, statusCode, usage) | ||
} | ||
|
||
if (req.url.startsWith('/stats/')) { | ||
const keyValue = req.url.split('/stats/')[1] | ||
const stats = await openkey.stats(keyValue) | ||
return send(res, 200, stats) | ||
} | ||
|
||
return send(res, 400) | ||
} catch (error) { | ||
console.log(' ' + styleText('red', `ERROR: ${error.message}`)) | ||
res.statusCode = 500 | ||
res.end() | ||
} | ||
}) | ||
|
||
const PORT = 1337 | ||
|
||
server.listen(PORT, () => { | ||
console.log(`Server is listening on port http://localhost:${PORT}`) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"scripts": { | ||
"dev": "watchexec --on-busy-update=restart --exts js,yml --clear=clear 'node index.js'" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
'use strict' | ||
|
||
const { isPlainObject } = require('./util') | ||
|
||
class OpenKeyError extends Error { | ||
constructor (props) { | ||
super() | ||
this.name = 'OpenKeyError' | ||
Object.assign(this, props) | ||
} | ||
} | ||
|
||
const errors = [ | ||
['KEY_NOT_EXIST', key => `The key \`${key}\` does not exist.`], | ||
['KEY_IS_ASSOCIATED', (id, value) => `The plan \`${id}\` is associated with the key \`${value}\`.`], | ||
['PLAN_NOT_EXIST', plan => `The plan \`${plan}\` does not exist.`], | ||
['PLAN_ID_REQUIRED', () => 'The argument `id` must be a string.'], | ||
['PLAN_INVALID_ID', () => 'The argument `id` cannot contain whitespace.'], | ||
['PLAN_INVALID_LIMIT', () => 'The argument `limit` must be a positive number.'], | ||
['PLAN_INVALID_PERIOD', () => 'The argument `period` must be a string.'], | ||
['PLAN_ALREADY_EXIST', plan => `The plan \`${plan}\` already exists.`], | ||
['METADATA_NOT_FLAT_OBJECT', () => 'The metadata must be a flat object.'], | ||
['METADATA_INVALID', key => `The metadata field '${key}' can't be an object.`] | ||
].reduce((acc, [code, message]) => { | ||
acc[code] = args => new OpenKeyError({ code, message: message.apply(null, args()) }) | ||
return acc | ||
}, {}) | ||
|
||
const assert = (condition, code, args = () => []) => { | ||
return ( | ||
condition || | ||
(() => { | ||
throw errors[code](args) | ||
})() | ||
) | ||
} | ||
|
||
const assertMetadata = metadata => { | ||
if (metadata) { | ||
assert(isPlainObject(metadata), 'METADATA_NOT_FLAT_OBJECT') | ||
Object.keys(metadata).forEach(key => { | ||
assert(!isPlainObject(metadata[key]), 'METADATA_INVALID', () => [key]) | ||
if (metadata[key] === undefined) delete metadata[key] | ||
}) | ||
return Object.keys(metadata).length ? metadata : undefined | ||
} | ||
} | ||
|
||
module.exports = { errors, assert, assertMetadata } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,10 +17,11 @@ testCleanup({ | |
keys: () => Promise.all([redis.keys(openkey.keys.prefixKey('*'))]) | ||
}) | ||
|
||
test('.create # `metadata` must be a flat object', async t => { | ||
test('.create # error if `metadata` is not a flat object', async t => { | ||
const error = await t.throwsAsync(openkey.keys.create({ metadata: { tier: { type: 'new' } } })) | ||
t.is(error.message, "The metadata field 'tier' can't be an object.") | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'METADATA_INVALID') | ||
}) | ||
|
||
test('.create # `metadata` as undefined is omitted', async t => { | ||
|
@@ -31,7 +32,8 @@ test('.create # `metadata` as undefined is omitted', async t => { | |
test('.create # error if plan does not exist', async t => { | ||
const error = await t.throwsAsync(openkey.keys.create({ plan: '123' })) | ||
t.is(error.message, 'The plan `123` does not exist.') | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'PLAN_NOT_EXIST') | ||
}) | ||
|
||
test('.create', async t => { | ||
|
@@ -94,14 +96,16 @@ test('.update', async t => { | |
test('.update # error if key does not exist', async t => { | ||
const error = await t.throwsAsync(openkey.keys.update('value')) | ||
t.is(error.message, 'The key `value` does not exist.') | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'KEY_NOT_EXIST') | ||
}) | ||
|
||
test('.update # error if plan does not exist', async t => { | ||
const { value } = await openkey.keys.create() | ||
const error = await t.throwsAsync(openkey.keys.update(value, { plan: 'id' })) | ||
t.is(error.message, 'The plan `id` does not exist.') | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'PLAN_NOT_EXIST') | ||
}) | ||
|
||
test('.update # add a plan', async t => { | ||
|
@@ -131,11 +135,12 @@ test('.update # add metadata', async t => { | |
} | ||
}) | ||
|
||
test('.update # metadata must be a flat object', async t => { | ||
test('.update # error is metadata is not a flat object', async t => { | ||
const { value } = await openkey.keys.create() | ||
const error = await t.throwsAsync(openkey.keys.update(value, { metadata: { email: { cc: '[email protected]' } } })) | ||
t.is(error.message, "The metadata field 'email' can't be an object.") | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'METADATA_INVALID') | ||
}) | ||
|
||
test('.update # metadata as undefined is omitted', async t => { | ||
|
@@ -192,5 +197,6 @@ test('.del # error if key does not exist', async t => { | |
const error = await t.throwsAsync(openkey.keys.del('key_id')) | ||
|
||
t.is(error.message, 'The key `key_id` does not exist.') | ||
t.is(error.name, 'TypeError') | ||
t.is(error.name, 'OpenKeyError') | ||
t.is(error.code, 'KEY_NOT_EXIST') | ||
}) |
Oops, something went wrong.