Database Connection

- added QueryHelper
- added DAO
- added `default-config.yaml`
- added auto-copying of the `default-config.yaml` to a `config.yaml` on startup if no config exists
pull/1/head
Trivernis 5 years ago
parent b7d10454ab
commit 0dcd590b80

1
.gitignore vendored

@ -5,3 +5,4 @@ npm-debug.log
test/*.log test/*.log
dist dist
.idea .idea
config.yaml

@ -7,3 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Connection to Postgres Database
- Graphql Schema
- default-config file and generation of config file on startup

@ -34,7 +34,12 @@ function compileSass() {
.pipe(dest('dist/public/stylesheets')); .pipe(dest('dist/public/stylesheets'));
} }
task('default', series(clearDist, compileTypescript, minifyJs, compileSass)); function moveRemaining() {
return src(['src/**/*', '!src/**/*.ts', '!src/**/*.sass', '!src/**/*.js'])
.pipe(dest('dist'));
}
task('default', series(clearDist, compileTypescript, minifyJs, compileSass, moveRemaining));
task('watch', () => { task('watch', () => {
watch('src/public/stylesheets/sass/**/*.sass', compileSass); watch('src/public/stylesheets/sass/**/*.sass', compileSass);
watch('**/*.js', minifyJs); watch('**/*.js', minifyJs);

13
package-lock.json generated

@ -137,6 +137,11 @@
"integrity": "sha512-UoCovaxbJIxagCvVfalfK7YaNhmxj3BQFRQ2RHQKLiu+9wNXhJnlbspsLHt/YQM99IaLUUFJNzCwzc6W0ypMeQ==", "integrity": "sha512-UoCovaxbJIxagCvVfalfK7YaNhmxj3BQFRQ2RHQKLiu+9wNXhJnlbspsLHt/YQM99IaLUUFJNzCwzc6W0ypMeQ==",
"dev": true "dev": true
}, },
"@types/js-yaml": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz",
"integrity": "sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA=="
},
"@types/mime": { "@types/mime": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
@ -370,7 +375,6 @@
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": { "requires": {
"sprintf-js": "~1.0.2" "sprintf-js": "~1.0.2"
} }
@ -1776,8 +1780,7 @@
"esprima": { "esprima": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
"dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.3", "version": "2.0.3",
@ -3815,7 +3818,6 @@
"version": "3.13.1", "version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": { "requires": {
"argparse": "^1.0.7", "argparse": "^1.0.7",
"esprima": "^4.0.0" "esprima": "^4.0.0"
@ -5913,8 +5915,7 @@
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
"dev": true
}, },
"sshpk": { "sshpk": {
"version": "1.16.1", "version": "1.16.1",

@ -40,6 +40,7 @@
"typescript": "^3.5.3" "typescript": "^3.5.3"
}, },
"dependencies": { "dependencies": {
"@types/js-yaml": "^3.12.1",
"@types/winston": "^2.4.4", "@types/winston": "^2.4.4",
"connect-pg-simple": "^6.0.0", "connect-pg-simple": "^6.0.0",
"cookie-parser": "^1.4.4", "cookie-parser": "^1.4.4",
@ -50,6 +51,7 @@
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"graphql": "^14.4.2", "graphql": "^14.4.2",
"graphql-import": "^0.7.1", "graphql-import": "^0.7.1",
"js-yaml": "^3.13.1",
"pg": "^7.12.1", "pg": "^7.12.1",
"socket.io": "^2.2.0", "socket.io": "^2.2.0",
"winston": "^3.2.1" "winston": "^3.2.1"

@ -1,6 +0,0 @@
database:
url: localhost
port: 54332
user: greenvironment
password: greendev
database: greenvironment

@ -0,0 +1,11 @@
# database connection info
database:
host:
port:
user:
password:
database:
# http server configuration
server:
port:

@ -1,3 +1,16 @@
import * as fsx from "fs-extra";
import App from "./app"; import App from "./app";
const configPath = "config.yaml";
const defaultConfig = __dirname + "/default-config.yaml";
/**
* async main function wrapper.
*/
(async () => {
if (!(await fsx.pathExists(configPath))) {
await fsx.copy(defaultConfig, configPath);
}
const app = new App(); const app = new App();
})();

@ -0,0 +1,19 @@
import {Pool} from "pg";
import globals from "./globals";
import {QueryHelper} from "./QueryHelper";
const config = globals.config;
export class DAO {
private queryHelper: QueryHelper;
constructor() {
const dbClient: Pool = new Pool({
database: config.database.database,
host: config.database.host,
password: config.database.password,
port: config.database.port,
user: config.database.user,
});
this.queryHelper = new QueryHelper(dbClient);
}
}

@ -0,0 +1,116 @@
import {Pool, PoolClient, QueryConfig, QueryResult} from "pg";
import globals from "./globals";
const logger = globals.logger;
export class SqlTransaction {
constructor(private client: PoolClient) {
}
/**
* Begins the transaction.
*/
public async begin() {
return await this.client.query("BEGIN");
}
/**
* Commits the transaction
*/
public async commit() {
return await this.client.query("COMMIT");
}
/**
* Rolls back the transaction
*/
public async rollback() {
return await this.client.query("ROLLBACK");
}
/**
* Executes a query inside the transaction.
* @param query
*/
public async query(query: QueryConfig) {
return await this.client.query(query);
}
/**
* Releases the client back to the pool.
*/
public async release() {
this.client.release();
}
}
export class QueryHelper {
private pool: Pool;
constructor(pgPool: Pool) {
this.pool = pgPool;
}
/**
* executes the sql query with values and returns all results.
* @param query
*/
public async all(query: QueryConfig): Promise<any[]> {
const result = await this.query(query);
return result.rows;
}
/**
* executes the sql query with values and returns the first result.
* @param query
*/
public async first(query: QueryConfig): Promise<any> {
const result = await this.query(query);
if (result.rows && result.rows.length > 0) {
return result.rows[0];
}
}
/**
* Creates a new Transaction to be uses with error handling.
*/
public async createTransaction() {
const client: PoolClient = await this.pool.connect();
return new SqlTransaction(client);
}
/**
* Queries the database with error handling.
* @param query - the sql and values to execute
*/
private async query(query: QueryConfig): Promise<QueryResult|{rows: any}> {
try {
return await this.pool.query(query);
} catch (err) {
logger.debug(`Error on query "${query}".`);
logger.error(`Sql query failed: ${err}`);
logger.verbose(err.stack);
return {
rows: null,
};
}
}
}
/**
* Returns the parameterized value sql for inserting
* @param columnCount
* @param rowCount
* @param [offset]
*/
export function buildSqlParameters(columnCount: number, rowCount: number, offset?: number): string {
let sql = "";
for (let i = 0; i < rowCount; i++) {
sql += "(";
for (let j = 0; j < columnCount; j++) {
sql += `$${(i * columnCount) + j + 1 + offset},`;
}
sql = sql.replace(/,$/, "") + "),";
}
return sql.replace(/,$/, "");
}

@ -0,0 +1,25 @@
import * as fsx from "fs-extra";
import * as yaml from "js-yaml";
import * as winston from "winston";
/**
* Defines global variables to be used.
*/
namespace globals {
export const config = yaml.safeLoad(fsx.readFileSync("config.yaml", "utf-8"));
export const logger = winston.createLogger({
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.colorize(),
winston.format.printf(({ level, message, label, timestamp }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
}),
),
}),
],
});
}
export default globals;

@ -18,7 +18,10 @@
"no-console": { "no-console": {
"severity": "warning", "severity": "warning",
"options": ["debug", "info", "log", "time", "timeEnd", "trace"] "options": ["debug", "info", "log", "time", "timeEnd", "trace"]
} },
"no-namespace": false,
"no-internal-module": false,
"max-classes-per-file": false
}, },
"jsRules": { "jsRules": {
"max-line-length": { "max-line-length": {

Loading…
Cancel
Save