This commit is contained in:
martin 2018-05-29 11:47:09 +01:00
parent bfca44fa45
commit 6c4e98fd0c
20 changed files with 929 additions and 0 deletions

147
test/.gitignore vendored Normal file
View File

@ -0,0 +1,147 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# 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)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
/src/bundle.js
/src/bundle.js.map
/setup/web/live/

46
test/docker-compose.yml Normal file
View File

@ -0,0 +1,46 @@
version: '3'
services:
PostgreSQL:
image: postgres:alpine
ports:
- 5432:5432
restart: always
env_file: .env
volumes:
- "$ROOT/postgresql:/var/lib/postgresql"
- ./setup/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
adminer:
image: adminer
restart: always
depends_on:
- PostgreSQL
ports:
- 8080:8080
redis:
image: redis:alpine
restart: always
volumes:
- "$ROOT/redis:/data"
web:
build: ./setup/web/.
command: npm run start
volumes:
- .:/user/app
- /usr/app/node_modules
ports:
- 9000:9000
depends_on:
- PostgreSQL
- redis
environment:
- HOST=${HOST}
- DATABASE=${POSTGRES_DB}
- USER=${POSTGRES_USER}
- PASSWORD=${POSTGRES_PASSWORD}
- DATABASE_URL=${DATABASE_URL}

View File

View File

@ -0,0 +1,54 @@
-- SEQUENCE: public.test_id_seq
-- DROP SEQUENCE public.test_id_seq;
CREATE SEQUENCE public.test_id_seq;
ALTER SEQUENCE public.test_id_seq
OWNER TO comcardeuser;
-- Table: public.test
-- DROP TABLE public.test;
CREATE TABLE public.test
(
id integer NOT NULL DEFAULT nextval('test_id_seq'::regclass),
company_name character varying(100) COLLATE pg_catalog."default",
CONSTRAINT test_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.test
OWNER to comcardeuser;
-- FUNCTION: public.insert_company(character varying)
-- DROP FUNCTION public.insert_company(character varying);
CREATE OR REPLACE FUNCTION public.insert_company(
_name character varying)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
INSERT into test(company_name) Values(_name);
END;
$BODY$;
ALTER FUNCTION public.insert_company(character varying)
OWNER TO comcardeuser;

View File

@ -0,0 +1,4 @@
#!/bin/bash
set -e
echo "Build Postgres"

View File

View File

@ -0,0 +1,9 @@
FROM node:alpine
WORKDIR /usr/app
COPY package.json .
COPY . .
RUN npm install --unsafe-perm

View File

@ -0,0 +1,33 @@
{
/**
* Application configuration section
* http://pm2.keymetrics.io/docs/usage/application-declaration/
*/
apps: [
// First application
{
"name": "Jubilee",
"script": "server.js",
"cwd": "/home/martind2000/dev/jubilee",
"watch": false,
"ignore_watch" : ["node_modules"],
"merge_logs" : true,
"autorestart" : true,
"restart_delay" : 3500,
"max_memory_restart" : "300M",
env: {
COMMON_VARIABLE: "true"
},
env_production: {
NODE_ENV: "production"
}
}
],
/**
* Deployment section
* http://pm2.keymetrics.io/docs/usage/deployment/
*/
deploy: {
}
}

View File

@ -0,0 +1,38 @@
'use strict';
const browserify = require('browserify');
const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const uglify = require('gulp-uglify-es').default;
const sourcemaps = require('gulp-sourcemaps');
const gutil = require('gulp-util');
const rename = require('gulp-rename');
const stripDebug = require('gulp-strip-debug');
gulp.task('bundleBackbone', function () {
// set up the browserify instance on a task basis
const b = browserify({
'debug': true,
'entries': './src/js/app.js'
});
return b.bundle()
.pipe(source('app.js'))
.pipe(buffer())
// .pipe(stripDebug())
.pipe(rename('bundle.js'))
.pipe(sourcemaps.init({ 'loadMaps': true }))
// Add transformation tasks to the pipeline here.
// .pipe(uglify())
.on('error', gutil.log)
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./live/js'));
});
gulp.task('buildBackbone', [ 'bundleBackbone'], function() {
gulp.watch('src/js/**/*.js', [ 'bundleBackbone']);
});

View File

@ -0,0 +1,59 @@
const gulp = require('gulp'),
autoprefixer = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
concat = require('gulp-concat'),
cache = require('gulp-cache'),
htmlmin = require('gulp-htmlmin'),
inject = require('gulp-inject'),
del = require('del'),
htmlreplace = require('gulp-html-replace');
const scss = require('gulp-scss');
const sass = require('gulp-sass');
const googleWebFonts = require('gulp-google-webfonts');
const fontOptions = { };
gulp.task('styles', function() {
return gulp.src(['node_modules/backbone.modal/backbone.modal.css', 'node_modules/backbone.modal/backbone.modal.theme.css'])
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
/* .pipe(gulp.dest('dist/css'))*/
/* .pipe(rename({suffix: '.min'}))*/
.pipe(concat('style.min.css'))
.pipe(cssnano())
.pipe(gulp.dest('live/css'));
});
gulp.task('copy', function() {
gulp.src(['src/img/**/*']).pipe(gulp.dest('live/img'));
gulp.src(['src/gfx/**/*']).pipe(gulp.dest('live/gfx'));
gulp.src(['src/index.html']).pipe(gulp.dest('live'));
});
gulp.task('clean', function() {
return del(['live']);
});
gulp.task('customMUI', function() {
return gulp.src(['src/css/custom.scss'])
.pipe(sass({ 'outputStyle': 'compressed' }).on('error', sass.logError))
// .pipe(cssnano())
.pipe(rename('mui.custom.css'))
// .pipe(gulp.dest(`${dest}/css`));
.pipe(gulp.dest('live/css'));
});
gulp.task('vendor', function() {
return gulp.src([
'node_modules/muicss/dist/js/mui.min.js'
])
.pipe(concat('vendor.js'))
/* .pipe(uglify({ 'mangle': false }))*/
.pipe(gulp.dest(`live/js`));
});

View File

@ -0,0 +1,7 @@
const gulp = require('gulp');
const requireDir = require('require-dir');
requireDir('./gulp');
gulp.task('default', ['bundleBackbone', 'styles', 'copy', 'customMUI', 'vendor']);

View File

@ -0,0 +1,63 @@
{
"name": "pgtest",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "mocha",
"postinstall": "gulp default",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"backbone": "^1.3.3",
"browserify": "^16.1.0",
"eslint": "^4.19.0",
"express": "^4.16.3",
"hh-mm-ss": "^1.2.0",
"jquery": "^3.3.1",
"lodash": "^4.17.5",
"log4js": "^2.5.3",
"memory-cache": "^0.2.0",
"moment": "^2.21.0",
"muicss": "^0.9.38",
"pg-promise": "^8.4.4",
"request-json": "^0.6.3",
"strict-uri-encode": "^2.0.0",
"string": "^3.3.3",
"sugar": "^2.0.4",
"uglifyify": "^4.0.5",
"underscore": "^1.9.0"
},
"devDependencies": {
"expect.js": "^0.3.1",
"gulp": "^3.9.1",
"gulp-google-webfonts": "^1.0.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^3.1.0",
"gulp-scss": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-strip-debug": "^3.0.0",
"gulp-uglify-es": "^1.0.1",
"lodash.assign": "^4.2.0",
"mocha": "^5.0.4",
"node-fetch": "^2.1.1",
"require-dir": "^1.0.0",
"requirejs": "^2.3.5",
"sinon": "^4.4.6",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.11.0",
"whatwg-fetch": "^2.0.4",
"gulp-autoprefixer": "^5.0.0",
"gulp-bump": "^3.1.0",
"gulp-cache": "^1.0.2",
"gulp-concat": "^2.6.1",
"gulp-cssnano": "^2.1.2",
"gulp-html-replace": "^1.6.2",
"gulp-htmlmin": "^4.0.0",
"gulp-inject": "^4.3.1",
"gulp-uglify": "^3.0.0"
}
}

49
test/setup/web/server.js Normal file
View File

@ -0,0 +1,49 @@
const express = require('express');
const path = require('path');
const logger = require('log4js').getLogger('Server');
const db = require('./server/db-connector').dbConnection;
const dbTestdata = require('./server/db-testdata')(db);
const nameGen = require('./server/name-gen');
logger.level = 'debug';
const app = express();
const port = process.env.PORT || 9000;
const sitePath = 'live';
app.use(express.static(path.join(__dirname, sitePath)));
// app.get('/weather', cache('15 minutes'), (req, res) => {
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
app.get('/data', (req, res) => {
dbTestdata.sqlGetSimpleList().then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
});
});
app.listen(port, (err) => {
if (err)
return logger.error('Server error:', err);
logger.info(`Test Server is listening on ${port}`);
const i = setInterval(()=>{
dbTestdata.sqlInsertData(nameGen.gen()).then((d) => {
logger.debug(d);
}).catch((e) => {
logger.error(e);
});
}, 15000)
});

View File

@ -0,0 +1,22 @@
const pgp = require('pg-promise')();
const connectionString = process.env.DATABASE_URL;
/*const cn = {
connectionString:connectionString
};*/
const cn = {
host: process.env.HOST,
port: 5432,
database: process.env.DATABASE,
user: process.env.USER,
password: process.env.PASSWORD
};
console.log('>> connectionString', cn);
exports.dbConnection = pgp(cn);

View File

@ -0,0 +1,40 @@
const logger = require('log4js').getLogger('db-testdata');
logger.level = 'debug';
module.exports = function(db) {
const module = {};
module.sqlGetSimpleList = function(id) {
return new Promise(function(resolve, reject) {
db.manyOrNone('select * from "test";', [id])
.then(function(d) {
// console.log(d);
return resolve(d);
})
.catch((err)=> {
// console.log(err);
return reject(err);
});
});
};
module.sqlInsertData = function(name) {
logger.info('Insert ', name);
return new Promise(function(resolve, reject) {
db.func('insert_company',
[name])
.then(()=> {
return resolve('ok');
})
.catch((err)=> {
return reject(err);
});
});
};
return module;
};

View File

@ -0,0 +1,61 @@
/**
* Created by mdonnel on 20/04/2017.
*/
Array.prototype.random = function () {
return this[Math.floor((Math.random() * this.length))];
};
const left = ['Alabama',
'Alaska',
'Arizona',
'Maryland',
'Nevada',
'Mexico',
'Texas',
'Utah',
'Glasgow',
'Inverness',
'Edinburgh',
'Dumbarton',
'Balloch',
'Renton',
'Cardross',
'Dundee',
'Paisley',
'Hamilton',
'Greenock',
'Falkirk',
'Irvine',
'Renfrew',
'Erskine',
'London',
'Hammersmith',
'Islington',
'Silver', 'Black', 'Yellow', 'Purple', 'White', 'Pink', 'Red', 'Orange', 'Brown', 'Green', 'Blue',
'Amber', 'Aqua', 'Azure', 'Bronze', 'Coral', 'Copper', 'Crimson', 'Cyan', 'Ginger', 'Gold', 'Indigo', 'Jade'
];
const right = ['Aganju', 'Cygni', 'Akeron', 'Antares', 'Aragoth', 'Ardus', 'Carpenter',
'Cooper', 'Dahin', 'Capella', 'Endriago', 'Gallina', 'Fenris', 'Freya', 'Glenn', 'Grissom',
'Jotunheim', 'Kailaasa', 'Lagarto', 'Muspelheim', 'Nifleheim', 'Primus', 'Vega', 'Ragnarok',
'Shepard', 'Slayton', 'Tarsis', 'Mercury', 'Venus', 'Mars', 'Earth', 'Terra', 'Jupiter',
'Saturn', 'Uranus', 'Neptune', 'Pluto', 'Europa', 'Ganymede', 'Callisto', 'Titan', 'Juno',
'Eridanus', 'Scorpius', 'Crux', 'Cancer', 'Taurus', 'Lyra', 'Andromeda', 'Virgo', 'Aquarius',
'Cygnus', 'Corvus', 'Taurus', 'Draco', 'Perseus', 'Pegasus', 'Gemini', 'Columbia', 'Bootes',
'Orion', 'Deneb', 'Merope', 'Agate', 'Amber', 'Beryl', 'Calcite', 'Citrine', 'Coral', 'Diamond',
'Emerald', 'Garnet', 'Jade', 'Lapis', 'Moonstone', 'Obsidian', 'Onyx', 'Opal', 'Pearl', 'Quartz',
'Ruby', 'Sapphire', 'Topaz', 'Iron', 'Lead', 'Nickel', 'Copper', 'Zinc', 'Tin', 'Manes', 'Argon',
'Neon', 'Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India', 'Juliett',
'Kilo', 'Lima', 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform',
'Victor', 'Whisky', 'Xray', 'Yankee', 'Zulu'];
function gen(){
return (`${left.random() } ${ right.random() }`);
}
module.exports = {gen};

View File

@ -0,0 +1,137 @@
// import MUI colors
@import "./node_modules/muicss/lib/sass/mui/colors";
// customize MUI variables
$mui-primary-color: mui-color('pink', '500');
$mui-primary-color-dark: mui-color('blue-grey', '700');
$mui-primary-color-light: mui-color('blue-grey', '100');
$mui-accent-color: mui-color('deep-purple', '900');
$mui-accent-color-dark: mui-color('indigo', 'A100');
$mui-accent-color-light: mui-color('indigo', 'A400');
$mui-base-font-family: 'Roboto', "Helvetica Neue", Helvetica, Arial, Verdana, "Trebuchet MS";
$mui-base-font-weight: 400;
$mui-appbar-font-color: mui-color('black') !default;
$mui-link-font-color: mui-color('pink', '900') !default;
// import MUI SASS
@import "./node_modules/muicss/lib/sass/mui";
body {
background-color: mui-color('grey', '100');
}
#header {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 2;
transition: left 0.2s;
}
ul {
margin: 0;
padding: 0;
}
li {
display: inline;
margin: 0;
padding: 0 4px 0 0;
}
.dates {
padding: 2px;
border: solid 1px #80007e;
background-color: #ffffff;
}
#btc, #fx, #trend {
font-size: 85%;
}
.up, .ontime, .trendUp {
color: mui-color('green') !important;
}
.down, .delayed, .trendDown {
color: $mui-text-danger !important;
}
.nochange {
color: #000000;
}
.password {
border: 1px solid mui-color('grey', '400');
background-color: mui-color('grey', '200');
font-family: monospace;
white-space: pre;
}
.trendUp:before {
content: "";
}
.trendDown:before {
content: ''
}
.card {
position: relative;
background-color: #fff;
min-height: 48px;
margin: 8px;
border-bottom-color: #666666;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.itemRow {
background-color: #fff;
min-height: 48px;
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
line-height: 48px;
}
.cardTitle {
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
}
.entry {
height: 36px;
margin: 6px 0;
vertical-align: middle;
}
.time {
font-family: 'Roboto';
}
.titleBar {
font-family: 'Gotham Light';
font-size: 125%;
}
.tableBody {
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.unsliced {
height: 455px;
}
.sliced {
height: 300px;
}

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PG TEST</title>
<link href="/css/mui.custom.css" rel="stylesheet" type="text/css"/>
<meta name="theme-color" content="#ffffff">
</head>
<body>
<div class="appPanel" data-id="main">
<div class="mui-container">
<div id="greet"></div>
<div id="location" class="mui-row" style="display: none;">PG TEST</div>
</div>
<div class="mui-container" id="viewFrame">
<div id="dataShell" class="mui-panel" style="display: ;">
<div id="dataTitle" class="mui--text-title cardTitle">Data</div>
<div id="data"></div>
</div>
</div>
</div>
<script src="js/vendor.js" async></script>
<script type='module' src="js/bundle.js" async></script>
<noscript>
<!-- anchor linking to external file -->
<h1>Javascript disabled</h1>
<p>This really requires javascript to work</p>
</noscript>
</body>
</html>

View File

@ -0,0 +1,97 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const DataItem = Backbone.Model.extend({
});
const DataCollection = Backbone.Collection.extend({
'model': DataItem
});
// const dataCollection = new dataCollection();
const DataItemView = Backbone.View.extend({
'tagName': 'div',
'className' : 'mui-row',
'template': _.template(`
<div class="mui-col-xs-4 mui-col-md-4"><%=id%></div>
<div class="mui-col-xs-8 mui-col-md-8"><%=company_name%></div>
`),
'initialize': function() {
this.render();
},
'render': function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
const DataListModel = Backbone.Model.extend({
'defaults' : function (obj) {
// return a new object
return {
'update' : new Date().getTime()
};
}, 'initialize': function() {
this.dataCollection = new DataCollection();
this.listenTo(this, 'change:update', this.onChange);
this.getData();
},
'onChange': function() {
this.getData();
},
'getData': function() {
request({
'url': `${window.loc.origin}/data`,
'method': 'GET', 'qs': {
}
}, function(err, res, body) {
if (err)
console.error(err);
else {
const fsJSON = JSON.parse(body);
this.dataCollection.reset(fsJSON);
this.logUpdate();
}
}.bind(this));
}, 'logUpdate': function() {
const time = new Date().getTime() ;
this.set('time', time);
this.timerID = setTimeout(
() => this.tick(),
30 * 1000
);
},
'tick': function() {
this.set('update', new Date().getTime());
}
});
const DataListView = Backbone.View.extend({
'initialize': function(options) {
this.model.dataCollection.bind('reset', this.render, this);
},
'render' : function() {
this.$el.empty();
this.model.dataCollection.each(function(item) {
const niView = new DataItemView({ 'model': item });
this.$el.append(niView.el);
}, this);
}
});
module.exports = { DataListModel, DataListView };

View File

@ -0,0 +1,19 @@
require('muicss');
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { DataListModel, DataListView } = require('./DataList');
var app = app || {};
(function () {
window.loc = new URL(window.location);
app.dataList = new DataListView({ 'model': new DataListModel(), 'el':'#data' });
})();