Refactored to typescript & more consistent code
Updated how we interface with figlet and unified error handling & response.
This commit is contained in:
31
.eslintrc.json
Normal file
31
.eslintrc.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"root": true,
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"eslint-config-airbnb-base",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"lines-between-class-members": [
|
||||
"error",
|
||||
"always",
|
||||
{ "exceptAfterSingleLine": true }
|
||||
],
|
||||
"max-classes-per-file": 1,
|
||||
"no-empty": [
|
||||
2,
|
||||
{
|
||||
"allowEmptyCatch": true
|
||||
}
|
||||
],
|
||||
"no-promise-executor-return": 1,
|
||||
"no-shadow": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
node_modules/
|
||||
lib/
|
||||
|
||||
.DS_Store
|
||||
|
||||
14
package.json
14
package.json
@@ -1,9 +1,19 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "node src/app.js"
|
||||
"build": "tsc",
|
||||
"start": "node lib/app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"express": "^4.18.2",
|
||||
"figlet": "^1.5.2"
|
||||
"figlet": "^1.5.2",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"@typescript-eslint/parser": "^5.40.1"
|
||||
}
|
||||
}
|
||||
|
||||
15
src/app.js
15
src/app.js
@@ -1,15 +0,0 @@
|
||||
const express = require("express");
|
||||
|
||||
const asciiFontController = require("./controllers/asciiController.js");
|
||||
const modtFontController = require("./controllers/motdController.js");
|
||||
const requiredQueryMiddleware = require("./requiredQueryMiddleware");
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
app.get("/ascii", requiredQueryMiddleware, asciiFontController);
|
||||
app.get("/motd", requiredQueryMiddleware, modtFontController);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Font generation application listening on port ${port}.`);
|
||||
});
|
||||
17
src/app.ts
Normal file
17
src/app.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import express from "express";
|
||||
|
||||
import figletontController from "./controllers/figletController";
|
||||
import modtFontController from "./controllers/motdController";
|
||||
import fontsController from "./controllers/fontsController";
|
||||
import requiredQueryMiddleware from "./requiredQueryMiddleware";
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
app.get("/figlet", requiredQueryMiddleware, figletontController);
|
||||
app.get("/motd", requiredQueryMiddleware, modtFontController);
|
||||
app.get("/fonts", fontsController);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Figlet generation application listening on port ${port}.`);
|
||||
});
|
||||
33
src/ascii.js
33
src/ascii.js
@@ -1,33 +0,0 @@
|
||||
const figlet = require("figlet");
|
||||
|
||||
const defaultFontOptions = {
|
||||
font: "Larry 3D",
|
||||
horizontalLayout: "default",
|
||||
verticalLayout: "default",
|
||||
width: 80,
|
||||
whitespaceBreak: true,
|
||||
};
|
||||
|
||||
function generateAscii(message, options) {
|
||||
const _options = {
|
||||
...defaultFontOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
figlet.text(message, _options, (err, data) => {
|
||||
if (err) {
|
||||
console.error("error from figlet:", error);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
if (_options.font === "Larry 3D") {
|
||||
data = data.replaceAll("L", "_");
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = generateAscii;
|
||||
@@ -1,16 +0,0 @@
|
||||
const generateAscii = require("../ascii.js");
|
||||
|
||||
const asciiFontController = (req, res) => {
|
||||
const { text } = req.query;
|
||||
|
||||
return generateAscii(text)
|
||||
.then((ascii) => res.send(ascii))
|
||||
.catch((error) =>
|
||||
res.status(error?.statusCode || 500).send({
|
||||
success: false,
|
||||
message: error?.message || "Unexpected error from font generation",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = asciiFontController;
|
||||
20
src/controllers/figletController.ts
Normal file
20
src/controllers/figletController.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import FigletFonts from "../figletFonts";
|
||||
import IFigletOptions from "../interfaces/IFigletOptions";
|
||||
|
||||
const figletFonts = new FigletFonts();
|
||||
|
||||
const figletTextController = async (req, res) => {
|
||||
const { text, font, width } = req.query;
|
||||
let options: IFigletOptions | Object = {};
|
||||
if (font) options = { ...options, font };
|
||||
if (width) options = { ...options, width };
|
||||
|
||||
figletFonts.generateText(text, options as IFigletOptions)
|
||||
.then(data => res.send(data))
|
||||
.catch((error) => {
|
||||
const msg = error?.message || "Unexpected error from generating figlet";
|
||||
res.status(error?.statusCode || 500).send(msg)
|
||||
});
|
||||
};
|
||||
|
||||
export default figletTextController;
|
||||
14
src/controllers/fontsController.ts
Normal file
14
src/controllers/fontsController.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import FigletFonts from "../figletFonts.js";
|
||||
|
||||
const figletFonts = new FigletFonts();
|
||||
|
||||
const fontsController = (req, res) => {
|
||||
return figletFonts.fonts
|
||||
.then((fonts) => res.send(fonts))
|
||||
.catch((error) => {
|
||||
const msg = error?.message || "Unexpected error from fetching fonts"
|
||||
res.status(error?.statusCode || 500).send(msg)
|
||||
});
|
||||
};
|
||||
|
||||
export default fontsController;
|
||||
@@ -1,17 +0,0 @@
|
||||
const generateAscii = require("../ascii.js");
|
||||
|
||||
const modtFontController = (req, res) => {
|
||||
const { text } = req.query;
|
||||
|
||||
return generateAscii(message)
|
||||
.then((ascii) => generateCatOutput(ascii))
|
||||
.then((motd) => res.send(motd))
|
||||
.catch((error) => {
|
||||
return res.status(500).send({
|
||||
success: false,
|
||||
message: "Unexpected error from font generation",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = modtFontController;
|
||||
29
src/controllers/motdController.ts
Normal file
29
src/controllers/motdController.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import FigletFonts from "../figletFonts";
|
||||
import IFigletOptions from "../interfaces/IFigletOptions";
|
||||
|
||||
const figletFonts = new FigletFonts()
|
||||
|
||||
function generateCatOutput(data: string) {
|
||||
return Promise.resolve(`#!/bin/sh
|
||||
cat << 'EOF'
|
||||
${data}
|
||||
EOF
|
||||
`)
|
||||
}
|
||||
|
||||
const modtFontController = (req, res) => {
|
||||
const { text, font, width } = req.query;
|
||||
let options: IFigletOptions | Object = {};
|
||||
if (font) options = { ...options, font };
|
||||
if (width) options = { ...options, width };
|
||||
|
||||
figletFonts.generateText(text, options as IFigletOptions)
|
||||
.then((data) => generateCatOutput(data))
|
||||
.then((motd) => res.send(motd))
|
||||
.catch((error) => {
|
||||
const msg = error?.message || "Unexpected error from generating motd figlet"
|
||||
res.status(error?.statusCode || 500).send(msg)
|
||||
});
|
||||
};
|
||||
|
||||
export default modtFontController;
|
||||
@@ -1,13 +0,0 @@
|
||||
class MissingTextError extends Error {
|
||||
constructor(error = null) {
|
||||
const message = `Missing query parameter 'text'`;
|
||||
super(message);
|
||||
|
||||
this.error = error;
|
||||
this.statusCode = 400;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MissingTextError,
|
||||
};
|
||||
47
src/errors.ts
Normal file
47
src/errors.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
class MissingTextError extends Error {
|
||||
error: string
|
||||
statusCode: number
|
||||
|
||||
constructor(error = null) {
|
||||
const message = `Missing query parameter 'text'.`;
|
||||
super(message);
|
||||
|
||||
this.error = error;
|
||||
this.statusCode = 400;
|
||||
}
|
||||
}
|
||||
|
||||
class FontNotFoundError extends Error {
|
||||
error: string
|
||||
statusCode: number
|
||||
|
||||
constructor(font, error = null) {
|
||||
const message = `Font '${font}' not found. Check /fonts.`;
|
||||
super(message);
|
||||
|
||||
this.error = error;
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
class UnexpectedFigletError extends Error {
|
||||
error: string
|
||||
statusCode: number
|
||||
|
||||
constructor(error) {
|
||||
const message = "Unexpected error from figlet!"
|
||||
super(message)
|
||||
|
||||
this.error = error;
|
||||
this.statusCode = 500
|
||||
|
||||
console.log(message)
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
MissingTextError,
|
||||
FontNotFoundError,
|
||||
UnexpectedFigletError
|
||||
};
|
||||
59
src/figletFonts.ts
Normal file
59
src/figletFonts.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import figlet from "figlet";
|
||||
import IFigletOptions from "./interfaces/IFigletOptions";
|
||||
import { UnexpectedFigletError } from './errors'
|
||||
|
||||
class FigletFonts {
|
||||
|
||||
cachedFonts: Array<string>;
|
||||
defaultFontOptions: IFigletOptions
|
||||
|
||||
constructor() {
|
||||
this.cachedFonts = [];
|
||||
this.defaultFontOptions = {
|
||||
font: "Larry 3D",
|
||||
horizontalLayout: "default",
|
||||
verticalLayout: "default",
|
||||
width: 80,
|
||||
whitespaceBreak: true,
|
||||
};
|
||||
}
|
||||
|
||||
static sanitizeLarry(text: string) {
|
||||
return text.replaceAll("L", "_");
|
||||
}
|
||||
|
||||
generateText(text: string, options: IFigletOptions): Promise<string> {
|
||||
const _options: IFigletOptions = {
|
||||
...this.defaultFontOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
figlet.text(text, _options, (err, data) => {
|
||||
if (err) {
|
||||
return reject(new UnexpectedFigletError(err));
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get fonts(): Promise<Array<string>> {
|
||||
if (this.cachedFonts.length > 0) {
|
||||
return Promise.resolve(this.cachedFonts);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
figlet.fonts((err, fonts) => {
|
||||
if (err) {
|
||||
return reject(new UnexpectedFigletError(err));
|
||||
}
|
||||
|
||||
resolve(fonts);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default FigletFonts;
|
||||
7
src/interfaces/IFigletOptions.ts
Normal file
7
src/interfaces/IFigletOptions.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default interface IFigletOptions {
|
||||
font: string
|
||||
horizontalLayout: string
|
||||
verticalLayout: string
|
||||
width: number
|
||||
whitespaceBreak: boolean
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
const { MissingTextError } = require("./errors.js");
|
||||
|
||||
const requiredQueryMiddleware = (req, res, next) => {
|
||||
const { text } = req.query;
|
||||
|
||||
if (!text || text?.length === 0) {
|
||||
const error = new MissingTextError();
|
||||
return res.status(error?.statusCode || 500).send(error?.message);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = requiredQueryMiddleware;
|
||||
30
src/requiredQueryMiddleware.ts
Normal file
30
src/requiredQueryMiddleware.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import FigletFonts from "./figletFonts";
|
||||
import { MissingTextError, FontNotFoundError } from "./errors";
|
||||
|
||||
const figletFonts = new FigletFonts();
|
||||
|
||||
async function doesFontExist(font: string) {
|
||||
const fontLowercased = font.toLowerCase();
|
||||
|
||||
const fonts: Array<string> = await figletFonts.fonts
|
||||
return fonts.findIndex((_font) => _font.toLowerCase() === fontLowercased) === -1;
|
||||
}
|
||||
|
||||
const requiredQueryMiddleware = async (req, res, next) => {
|
||||
const { text, font = "" } = req.query;
|
||||
let error = null;
|
||||
|
||||
if (!text || text?.length === 0) {
|
||||
error = new MissingTextError();
|
||||
}
|
||||
|
||||
if (font && (await doesFontExist(font))) {
|
||||
error = new FontNotFoundError(font, error);
|
||||
}
|
||||
|
||||
if (error) return res.status(error?.statusCode || 500).send(error?.message);
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
export default requiredQueryMiddleware;
|
||||
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"],
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src/**/*.js", "src/**/*.ts", "src/**/*.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user