diff --git a/.gitignore b/.gitignore index 8fdf499e..887eed61 100644 --- a/.gitignore +++ b/.gitignore @@ -2,14 +2,12 @@ logs *.log npm-debug.log* -yarn-debug.log* -yarn-error.log* +.idea # Runtime data pids *.pid *.seed -*.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov @@ -23,39 +21,43 @@ coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt -# Bower dependency directory (https://bower.io/) -bower_components - # node-waf configuration .lock-wscript -# Compiled binary addons (https://nodejs.org/api/addons.html) +# Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ +node_modules +jspm_packages # Optional npm cache directory .npm -# Optional eslint cache -.eslintcache - # Optional REPL history .node_repl_history -# Output of 'npm pack' -*.tgz +# 0x +profile-* + +# mac files +.DS_Store + +# vim swap files +*.swp + +# webstorm +.idea -# Yarn Integrity file -.yarn-integrity +# vscode +.vscode +*code-workspace -# dotenv environment variables file -.env +# clinic +profile* +*clinic* +*flamegraph* -# next.js build output -.next \ No newline at end of file +# generated code +examples/typescript-server.js +test/types/index.js diff --git a/.releaserc.js b/.releaserc.js new file mode 100644 index 00000000..0059af09 --- /dev/null +++ b/.releaserc.js @@ -0,0 +1,54 @@ +// in ".releaserc.js" or "release.config.js" + +const {promisify} = require('util') +const dateFormat = require('dateformat') +const readFileAsync = promisify(require('fs').readFile) +const path = require('path') +// Given a `const` variable `TEMPLATE_DIR` which points to "/lib/assets/templates" +const TEMPLATE_DIR = 'node_modules/semantic-release-gitmoji/lib/assets/templates' +// the *.hbs template and partials should be passed as strings of contents +const template = readFileAsync(path.join(TEMPLATE_DIR, 'default-template.hbs')) +const commitTemplate = readFileAsync(path.join(TEMPLATE_DIR, 'commit-template.hbs')) + +module.exports = { + branch: "master", + generateNotes: ['semantic-release-gitmoji'], + analyzeCommits: ['semantic-release-gitmoji'], + plugins: [ + [ + 'semantic-release-gitmoji', { + releaseRules: { + major: [':boom:'], + minor: [':sparkles:'], + patch: [ + ':bug:', + ':ambulance:', + ':lock:' + ] + }, + releaseNotes: { + template: template, + partials: {commitTemplate}, + helpers: { + datetime: function (format = 'UTC:yyyy-mm-dd') { + return dateFormat(new Date(), format) + } + }, + issueResolution: { + template: '{baseUrl}/{owner}/{repo}/issues/{ref}', + baseUrl: 'https://github.com', + source: 'github.com' + } + } + } + ], + "@semantic-release/release-notes-generator", + ["@semantic-release/changelog", { + "changelogFile": "CHANGELOG.md" + }], + ['@semantic-release/git', { + "assets": ["CHANGELOG.md", "package.json", "packages"], + "message": ":bookmark: (release) ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + }] + ] +} \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 00000000..cc9b2216 --- /dev/null +++ b/app.ts @@ -0,0 +1,30 @@ +'use strict' + +const path = require('path') +const AutoLoad = require('fastify-autoload') + +module.exports = function (fastify, opts, next) { + // Place here your custom code! + + // Do not touch the following lines + + // This loads all plugins defined in plugins + // those should be support plugins that are reused + // through your application + fastify.register(AutoLoad, { + dir: path.join(__dirname, 'plugins'), + includeTypeScript: true, + options: Object.assign({}, opts) + }) + + // This loads all plugins defined in services + // define your routes in one of these + fastify.register(AutoLoad, { + dir: path.join(__dirname, 'services'), + includeTypeScript: true, + options: Object.assign({}, opts) + }) + + // Make sure to call next when done + next() +} diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..034ec53b --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,9 @@ +module.exports = { + extends: ["gitmoji"], + parserPreset: { + parserOpts: { + headerPattern: /^(:\w*:)(?:\s)(?:\((.*?)\))?\s((?:.*(?=\())|.*)(?:\(#(\d*)\))?/, + headerCorrespondence: ["type", "scope", "subject", "ticket"] + } + } +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..abec2f9e --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "@hospitalrun-org/server", + "version": "1.0.0", + "description": "Fastify Application", + "main": "app.ts", + "directories": { + "test": "test" + }, + "scripts": { + "test": "ts-node ./node_modules/tap/bin/run.js test/**/*.test.ts", + "start": "ts-node ./node_modules/fastify-cli/cli.js start -l info app.ts", + "dev": "ts-node ./node_modules/fastify-cli/cli.js start -l info -P app.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "fastify": "^2.0.0", + "fastify-plugin": "^1.5.0", + "fastify-autoload": "^1.0.0", + "fastify-cli": "^1.2.0" + }, + "devDependencies": { + "@commitlint/cli": "^8.1.0", + "@commitlint/core": "^8.1.0", + "@semantic-release/changelog": "^3.0.4", + "@semantic-release/git": "^7.0.16", + "@semantic-release/github": "^5.4.3", + "@semantic-release/npm": "^5.1.15", + "@semantic-release/release-notes-generator": "^7.3.0", + "@types/node": "^12.7.4", + "commitizen": "^4.0.3", + "commitlint-config-gitmoji": "^1.0.1", + "cz-emoji": "^1.1.2", + "husky": "^3.0.5", + "semantic-release": "^15.13.24", + "semantic-release-cli": "^5.2.1", + "semantic-release-gitmoji": "^1.3.2", + "tap": "^12.5.3", + "ts-node": "^8.3.0", + "typescript": "^3.6.2" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-emoji" + } + }, + "husky": { + "hooks": { + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", + "pre-commit": "npm test" + } + } +} diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 00000000..ad1734bb --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,16 @@ +# Plugins Folder + +Plugins define behavior that is common to all the routes in your +application. Authentication, caching, templates, and all the other cross +cutting concerns should be handled by plugins placed in this folder. + +Files in this folder are typically defined through the +[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module, +making them non-encapsulated. They can define decorators and set hooks +that will then be used in the rest of your application. + +Check out: + +* [The hitchhiker's guide to plugins](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md) +* [Fastify decorators](https://www.fastify.io/docs/latest/Decorators/). +* [Fastify lifecycle](https://www.fastify.io/docs/latest/Lifecycle/). diff --git a/plugins/support.ts b/plugins/support.ts new file mode 100644 index 00000000..7f8d2a55 --- /dev/null +++ b/plugins/support.ts @@ -0,0 +1,11 @@ +'use strict' + +import * as fp from 'fastify-plugin' + + +export default fp((fastify, opts, next) => { + fastify.decorate('someSupport', function () { + return 'hugs' + }) + next() +}) \ No newline at end of file diff --git a/services/README.md b/services/README.md new file mode 100644 index 00000000..a463abb2 --- /dev/null +++ b/services/README.md @@ -0,0 +1,24 @@ +# Services Folder + +Services define routes within your application. Fastify provides an +easy path to a microservice architecture, in the future you might want +to independently deploy some of those. + +In this folder you should define all the services that define the routes +of your web application. +Each service is a [Fastify +plugin](https://www.fastify.io/docs/latest/Plugins/), it is +encapsulated (it can have its own independent plugins) and it is +typically stored in a file; be careful to group your routes logically, +e.g. all `/users` routes in a `users.js` file. We have added +a `root.js` file for you with a '/' root added. + +If a single file become too large, create a folder and add a `index.js` file there: +this file must be a Fastify plugin, and it will be loaded automatically +by the application. You can now add as many files as you want inside that folder. +In this way you can create complex services within a single monolith, +and eventually extract them. + +If you need to share functionality between services, place that +functionality into the `plugins` folder, and share it via +[decorators](https://www.fastify.io/docs/latest/Decorators/). diff --git a/services/example/index.ts b/services/example/index.ts new file mode 100644 index 00000000..3384f5cc --- /dev/null +++ b/services/example/index.ts @@ -0,0 +1,17 @@ +'use strict' + +module.exports = function (fastify, opts, next) { + fastify.get('/example', function (request, reply) { + reply.send('this is an example') + }) + + next() +} + +// If you prefer async/await, use the following +// +// module.exports = async function (fastify, opts) { +// fastify.get('/example', async function (request, reply) { +// return 'this is an example' +// }) +// } diff --git a/services/root.ts b/services/root.ts new file mode 100644 index 00000000..3e9dc2a8 --- /dev/null +++ b/services/root.ts @@ -0,0 +1,11 @@ +'use strict' +import * as fastify from 'fastify' +import { Server, IncomingMessage, ServerResponse } from 'http' + + +export default (fastify: fastify.FastifyInstance, opts, next) => { + fastify.get('/', (request, reply) => { + reply.send({ root: true }) + }) + next() +} \ No newline at end of file diff --git a/test/helper.ts b/test/helper.ts new file mode 100644 index 00000000..83007557 --- /dev/null +++ b/test/helper.ts @@ -0,0 +1,34 @@ +'use strict' + +// This file contains code that we reuse +// between our tests. + +const Fastify = require('fastify') +const fp = require('fastify-plugin') +const App = require('../app') + +// Fill in this config with all the configurations +// needed for testing the application +export const config = () => { + return {} +} + +// automatically build and tear down our instance +export const build = (t) => { + const app = Fastify() + + // fastify-plugin ensures that all decorators + // are exposed for testing purposes, this is + // different from the production setup + app.register(fp(App), config()) + + // tear down our app after we are done + t.tearDown(app.close.bind(app)) + + return app +} + +export default { + config, + build +} diff --git a/test/plugins/support.test.ts b/test/plugins/support.test.ts new file mode 100644 index 00000000..c268a44e --- /dev/null +++ b/test/plugins/support.test.ts @@ -0,0 +1,24 @@ +const { test } = require('tap') +const Fastify = require('fastify') +import Support from '../../plugins/support' + +test('support works standalone', (t) => { + t.plan(2) + const fastify = Fastify() + fastify.register(Support) + + fastify.ready((err) => { + t.error(err) + t.equal(fastify.someSupport(), 'hugs') + }) +}) + +// If you prefer async/await, use the following +// +// test('support works standalone', async (t) => { +// const fastify = Fastify() +// fastify.register(Support) +// +// await fastify.ready() +// t.equal(fastify.someSupport(), 'hugs') +// }) diff --git a/test/services/example.test.ts b/test/services/example.test.ts new file mode 100644 index 00000000..a4a7ea90 --- /dev/null +++ b/test/services/example.test.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env ts-node + +const { test } = require('tap') +const { build } = require('../helper') + +test('example is loaded', (t) => { + t.plan(2) + const app = build(t) + + app.inject({ + url: '/example' + }, (err, res) => { + t.error(err) + t.equal(res.payload, 'this is an example') + }) +}) + +// If you prefer async/await, use the following +// +// test('example is loaded', async (t) => { +// const app = build(t) +// +// const res = await app.inject({ +// url: '/example' +// }) +// t.equal(res.payload, 'this is an example') +// }) diff --git a/test/services/root.test.ts b/test/services/root.test.ts new file mode 100644 index 00000000..37c52c60 --- /dev/null +++ b/test/services/root.test.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env ts-node + +const { test } = require('tap') +const { build } = require('../helper') + +test('default root route', (t) => { + t.plan(2) + const app = build(t) + + app.inject({ + url: '/' + }, (err, res) => { + t.error(err) + t.deepEqual(JSON.parse(res.payload), { root: true }) + }) +}) + +// If you prefer async/await, use the following +// +// test('default root route', async (t) => { +// const app = build(t) +// +// const res = await app.inject({ +// url: '/' +// }) +// t.deepEqual(JSON.parse(res.payload), { root: true }) +// }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..7006dda5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "lib": ["es2016"], + "paths": { + "*": ["src/*"] + }, + "allowJs": true, + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "./dist" + }, + "include": ["./src/**/*.ts"], + "exclude": ["node_modules"] +} \ No newline at end of file