Compare commits

..

20 Commits

Author SHA1 Message Date
8c1730389c
database configuration 2021-01-15 17:19:18 +01:00
4dad188a07
Merge branch 'master' of ssh://home.hottis.de:2922/wolutator/hausverwaltung 2021-01-15 17:03:43 +01:00
b9031157fb
add Dockerfile 2021-01-15 17:03:33 +01:00
eac10f8484
Merge branch 'master' of https://home.hottis.de/gitlab/wolutator/hausverwaltung into master 2021-01-15 16:40:30 +01:00
f88d3b5b39
last changes 2021-01-15 16:40:20 +01:00
e031660df9
webservice changes 2021-01-15 16:19:05 +01:00
24482da680
database in service 2021-01-15 12:53:46 +01:00
ceadf3338f
error handling in ui 2021-01-15 12:23:25 +01:00
124fca001a
changes in service 2021-01-15 12:22:40 +01:00
9717d171bf
refactor 2021-01-14 21:52:56 +01:00
521e916bbd
hero put 2021-01-14 17:29:17 +01:00
6aec708c84
Merge branch 'master' of https://home.hottis.de/gitlab/wolutator/hausverwaltung into master 2021-01-14 17:10:48 +01:00
53a93a948d
corsified server 2021-01-14 17:10:26 +01:00
8374e1831b
now it works, it was CORS 2021-01-14 17:00:56 +01:00
989e617ffe
fix 2021-01-14 13:46:44 +01:00
1b756cc090
fixes 2021-01-14 13:23:12 +01:00
9570bef24d
http 2021-01-14 12:51:40 +01:00
d4caaae0f2
not working 2021-01-13 15:40:43 +01:00
da51077869
works 2021-01-13 15:23:04 +01:00
845f3fd400
await 2021-01-13 13:02:27 +01:00
20 changed files with 1928 additions and 2055 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build && cp ./src/electron-app.js ./dist",
"electron-start": "./node_modules/.bin/electron dist/electron-app.js",
"build": "ng build",
"build-prod": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
@ -17,6 +17,7 @@
"@angular/compiler": "~11.0.6",
"@angular/core": "~11.0.6",
"@angular/forms": "~11.0.6",
"@angular/http": "^7.2.16",
"@angular/platform-browser": "~11.0.6",
"@angular/platform-browser-dynamic": "~11.0.6",
"@angular/router": "~11.0.6",
@ -29,9 +30,8 @@
"@angular/cli": "~11.0.6",
"@angular/compiler-cli": "~11.0.6",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@types/node": "^12.19.13",
"codelyzer": "^6.0.0",
"electron": "^11.1.1",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.1.0",

View File

@ -1,7 +1,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
@ -20,7 +20,8 @@ import { DashboardComponent } from './dashboard/dashboard.component';
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
import { MessageService } from '../message.service';
@Component({
selector: 'app-dashboard',
@ -10,14 +11,18 @@ import { HeroService } from '../hero.service';
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
constructor(private heroService: HeroService, private messageService: MessageService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes.slice(1, 5));
async getHeroes(): Promise<void> {
try {
this.heroes = await this.heroService.getHeroes();
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
}

View File

@ -4,6 +4,7 @@ import { Location } from '@angular/common';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
import { MessageService } from '../message.service';
@Component({
selector: 'app-hero-detail',
@ -14,22 +15,31 @@ export class HeroDetailComponent implements OnInit {
@Input() hero: Hero;
constructor(private route: ActivatedRoute, private heroService: HeroService, private location: Location) { }
constructor(private route: ActivatedRoute, private heroService: HeroService, private location: Location, private messageService: MessageService) { }
ngOnInit(): void {
this.getHero();
}
getHero(): void {
const id = +this.route.snapshot.paramMap.get('id');
this.heroService.getHero(id).subscribe(hero => this.hero = hero);
async getHero(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id');
this.hero = await this.heroService.getHero(id)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
goBack(): void {
this.location.back();
}
save(): void {
this.heroService.updateHero(this.hero).subscribe(() => this.goBack());
async save(): Promise<void> {
try {
await this.heroService.updateHero(this.hero)
this.goBack()
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
}

View File

@ -1,29 +1,28 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';
@Injectable({
providedIn: 'root'
})
export class HeroService {
constructor(private messageService: MessageService) { }
getHeroes(): Observable<Hero[]> {
constructor(private messageService: MessageService, private http: HttpClient) { }
getHeroes(): Promise<Hero[]> {
this.messageService.add('HeroService: fetched heroes');
return of(HEROES);
return this.http.get<Hero[]>(`http://172.16.3.185:5000/heroes/heroes`).toPromise()
}
getHero(id: number): Observable<Hero> {
getHero(id: number): Promise<Hero> {
this.messageService.add(`HeroService: fetch hero id=${id}`);
return of(HEROES.find(hero => hero.id === id));
return this.http.get<Hero>(`http://172.16.3.185:5000/heroes/hero/${id}`).toPromise()
}
updateHero(hero: Hero): Observable<any> {
updateHero(hero: Hero): Promise<any> {
this.messageService.add(`HeroService: save change: id=${hero.id}, name=${hero.name}`);
return of(true);
return this.http.put<Hero>(`http://172.16.3.185:5000/heroes/hero/${hero.id}`, hero).toPromise()
}
}

View File

@ -18,8 +18,12 @@ export class HeroesComponent implements OnInit {
constructor(private heroService: HeroService, private messageService: MessageService) { }
getHeroes(): void {
this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
async getHeroes() {
try {
this.heroes = await this.heroService.getHeroes();
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
ngOnInit(): void {

View File

@ -1,30 +0,0 @@
const { create } = require('domain')
const { app, BrowserWindow } = require('electron')
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: false
})
mainWindow.loadURL('file://' + __dirname + '/angular-tour-of-heroes/index.html')
mainWindow.webContents.openDevTools()
mainWindow.on('closed', () => {
mainWindow = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>AngularTourOfHeroes</title>
<base href="./">
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

View File

@ -1,27 +1,64 @@
"use strict";
const mariadb = require('mariadb');
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const mariadb_1 = __importDefault(require("mariadb"));
let conn;
mariadb.createConnection({
host: 'database',
user: 'heroes',
password: 'test123',
database: 'heroes'
})
.then(conn => {
conn.query("SELECT * FROM hero")
.then((rows) => {
console.log(rows);
return rows;
class DbHandle {
constructor() { }
async connect() {
this._conn = await mariadb_1.default.createConnection({
host: '172.16.10.18',
user: 'heroes',
password: 'test123',
database: 'heroes'
});
}
async getHeroes() {
var _a, _b;
const result = (_b = await ((_a = this._conn) === null || _a === void 0 ? void 0 : _a.query("SELECT id, name FROM hero"))) !== null && _b !== void 0 ? _b : Promise.reject(new Error('Connection not ready 1'));
return result.map((row) => {
var _a;
return { id: row.id, name: (_a = row.name) !== null && _a !== void 0 ? _a : 'unknown' };
});
}
close() {
var _a, _b;
return (_b = (_a = this._conn) === null || _a === void 0 ? void 0 : _a.end()) !== null && _b !== void 0 ? _b : Promise.reject(new Error('Connection not ready 2'));
}
}
/*
const dbHandle = new DbHandle()
dbHandle.connect()
.then(() => dbHandle.get("SELECT * FROM hero"))
.then((result) => {
console.log(result)
return dbHandle.close()
})
.then((res) => {
console.log(res);
conn.end();
.catch((err) => {
console.log(err)
})
.catch(err => {
console.log(err);
conn.end();
});
})
.catch(err => {
console.log('not connected');
});
*/
async function exec() {
const dbHandle = new DbHandle();
try {
await dbHandle.connect();
const heroes = await dbHandle.getHeroes();
console.log(heroes);
}
finally {
await dbHandle.close();
}
}
exec().catch((err) => console.log(err.message));
/*
dbHandle.get("SELECT * FROM hero")
.then((result) => {
console.log(result)
return dbHandle.close()
})
.catch((err) => {
console.log(err.message)
})
*/

View File

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"build": "rm -rf ./build && tsc",
"build": "tsc",
"start": "npm run build && node build/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},

View File

@ -1,31 +1,75 @@
const mariadb = require('mariadb')
import mariadb from 'mariadb'
import { stringify } from 'querystring'
let conn : mariadb.Connection
mariadb.createConnection({
host: 'database',
user: 'heroes',
password: 'test123',
database: 'heroes'
})
.then(conn => {
conn.query("SELECT * FROM hero")
.then((rows) => {
console.log(rows)
return rows
})
.then((res) => {
console.log(res)
conn.end()
})
.catch(err => {
console.log(err)
conn.end()
})
})
.catch(err => {
console.log('not connected')
})
interface Hero {
id: number
name: string
}
class DbHandle {
private _conn? : mariadb.Connection
public constructor() {}
public async connect() : Promise<void> {
this._conn = await mariadb.createConnection({
host: '172.16.10.18',
user: 'heroes',
password: 'test123',
database: 'heroes'
})
}
public async getHeroes() : Promise<Hero[]> {
const result = await this._conn?.query("SELECT id, name FROM hero") ?? Promise.reject(new Error('Connection not ready 1'))
return result.map((row: Record<string, unknown>) => {
return { id: row.id, name: row.name ?? 'unknown' }
})
}
public close() : Promise<void> {
return this._conn?.end() ?? Promise.reject(new Error('Connection not ready 2'))
}
}
/*
const dbHandle = new DbHandle()
dbHandle.connect()
.then(() => dbHandle.get("SELECT * FROM hero"))
.then((result) => {
console.log(result)
return dbHandle.close()
})
.catch((err) => {
console.log(err)
})
*/
async function exec() : Promise<void> {
const dbHandle = new DbHandle()
try {
await dbHandle.connect()
const heroes : Hero[] = await dbHandle.getHeroes()
console.log(heroes)
} finally {
await dbHandle.close()
}
}
exec().catch((err) => console.log(err.message))
/*
dbHandle.get("SELECT * FROM hero")
.then((result) => {
console.log(result)
return dbHandle.close()
})
.catch((err) => {
console.log(err.message)
})
*/

1
tools/ws/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/

36
tools/ws/Dockerfile Normal file
View File

@ -0,0 +1,36 @@
FROM python:latest
LABEL Maintainer="Wolfgang Hottgenroth wolfgang.hottgenroth@icloud.com"
ARG APP_DIR="/opt/app"
ARG CONF_DIR="${APP_DIR}/config"
RUN \
apt update && \
apt install -y libmariadbclient-dev && \
pip3 install mariadb && \
pip3 install connexion && \
pip3 install connexion[swagger-ui] && \
pip3 install uwsgi && \
pip3 install flask-cors
RUN \
mkdir -p ${APP_DIR} && \
mkdir -p ${CONF_DIR} && \
useradd -d ${APP_DIR} -u 1000 user
COPY *.py ${APP_DIR}/
COPY swagger.yaml ${APP_DIR}/
COPY server.ini ${CONF_DIR}/
USER 1000:1000
WORKDIR ${APP_DIR}
VOLUME ${CONF_DIR}
EXPOSE 5000
EXPOSE 9191
CMD [ "uwsgi", "./config/server.ini" ]

19
tools/ws/dbpool.py Normal file
View File

@ -0,0 +1,19 @@
import mariadb
pool = None
def createConnectionPool(config):
global pool
pool = mariadb.ConnectionPool(
user = config['user'],
password = config['password'],
host = config['host'],
database = config['database'],
pool_name = 'heroes-wep-app',
pool_size = 5
)
def getConnection():
global pool
return pool.get_connection()

2
tools/ws/greeting.py Normal file
View File

@ -0,0 +1,2 @@
def say_hello(name=None):
return { "message": "Hello {}, from API!".format(name or "") }

63
tools/ws/heroes.py Normal file
View File

@ -0,0 +1,63 @@
from dbpool import getConnection
def get_heroes():
try:
dbh = getConnection()
heroes = []
cur = dbh.cursor()
cur.execute("SELECT id, name FROM hero")
for (id, name) in cur:
heroes.append({"id": id, "name": name})
return heroes
except Exception as err:
return str(err), 500
finally:
dbh.close()
def get_hero(id=None):
try:
dbh = getConnection()
cur = dbh.cursor()
cur.execute("SELECT id, name FROM hero WHERE id = ?", (id,))
hero = None
try:
(id, name) = cur.next()
hero = { "id": id, "name": name }
print("x1: {}\n".format(hero))
except StopIteration:
return "Hero not found", 404
try:
(id, name) = cur.next()
return "More than one hero by that id ({}, {})".format(id, name), 500
except:
pass
return hero
except Exception as err:
return str(err), 500
finally:
dbh.close()
def put_hero(id=None, hero=None):
try:
dbh = getConnection()
cur = dbh.cursor()
cur.execute("UPDATE hero SET name = ? WHERE id = ?", (hero["name"], id))
dbh.commit()
return 'Hero updated', 200
except StopIteration:
return 'Hero not found', 404
except Exception as err:
return str(err), 500
finally:
dbh.close()
def post_hero(hero=None):
try:
newHeroId = len(HEROES)
hero["id"] = newHeroId
HEROES.append(hero)
return 'Hero inserted', 201
except Exception as err:
return str(err), 403

13
tools/ws/server.ini Normal file
View File

@ -0,0 +1,13 @@
[uwsgi]
http = :5000
wsgi-file = server.py
processes = 4
threads = 2
stats = :9191
[database]
host = 172.16.10.18
user = heroes
password = test123
database = heroes

22
tools/ws/server.py Normal file
View File

@ -0,0 +1,22 @@
import connexion
from flask_cors import CORS
from dbpool import createConnectionPool
import configparser
# load configuration
config = configparser.ConfigParser()
config.read('./config/server.ini')
# prepare database connections
createConnectionPool(config['database'])
# instantiate the webservice
app = connexion.App(__name__)
app.add_api('swagger.yaml')
# CORSify it - otherwise Angular won't accept it
CORS(app.app)
# provide the webservice application to uwsgi
application = app.app

116
tools/ws/swagger.yaml Normal file
View File

@ -0,0 +1,116 @@
swagger: '2.0'
info:
title: Heroes
version: "0.1"
paths:
/greeting/hello:
get:
tags: [ "greeting" ]
operationId: greeting.say_hello
summary: Returns a greeting
parameters:
- name: name
in: query
type: string
responses:
200:
description: Successful response.
schema:
type: object
properties:
message:
type: string
description: Message greeting
/heroes/hero/{id}:
get:
tags: [ "heroes" ]
operationId: heroes.get_hero
summary: Returns hero by id
parameters:
- name: id
in: path
type: integer
required: true
responses:
200:
description: Successful response.
schema:
$ref: '#/definitions/Hero'
404:
description: Hero not found
500:
description: Some server error
put:
tags: [ "heroes" ]
operationId: heroes.put_hero
summary: Update a hero
parameters:
- name: id
in: path
type: integer
required: true
- name: hero
in: body
required: true
schema:
$ref: '#/definitions/Hero'
responses:
200:
description: Hero updated
403:
description: Some error
409:
description: Duplicate name
404:
description: Hero not found
500:
description: Some server error
/heroes/hero:
post:
tags: [ "heroes" ]
operationId: heroes.post_hero
summary: Insert a hero
parameters:
- name: hero
in: body
required: true
schema:
$ref: '#/definitions/Hero'
responses:
200:
description: Hero inserted
403:
description: Some error
409:
description: Duplicate name
500:
description: Some server error
/heroes/heroes:
get:
tags: [ "heroes" ]
operationId: heroes.get_heroes
summary: Returns all heroes
responses:
200:
description: Successful response.
schema:
type: array
items:
$ref: '#/definitions/Hero'
404:
description: No heroes available
500:
description: Some server error
definitions:
Hero:
description: Hero type
type: object
properties:
id:
type: integer
name:
type: string