Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6c33534f1
|
|||
67c97ed4bc
|
|||
81ba2655b3
|
|||
1e9d22483c
|
|||
daca7eca9b
|
|||
32a66dc189
|
|||
3dd00d6dcd
|
|||
44b58bc48c
|
|||
1bddff4a23
|
|||
b500b81d80
|
|||
5937c99eb4
|
|||
dfc5c8421a
|
|||
b30e587c34
|
|||
3d0e602ee6
|
|||
30aa514495
|
@ -57,5 +57,5 @@ deploy:
|
||||
- docker rm $CONTAINER_NAME || echo "$CONTAINER_NAME not exsting, anyway okay"
|
||||
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY;
|
||||
- docker pull $IMAGE_NAME:${CI_COMMIT_TAG}
|
||||
- docker run -d --restart always --name "hv2-api" --network docker-server --ip 172.16.10.38 -v $SERVICE_VOLUME:/opt/app/config $IMAGE_NAME:${CI_COMMIT_TAG}
|
||||
- docker run -d --restart always --name $CONTAINER_NAME --network docker-server --ip 172.16.10.38 -v $SERVICE_VOLUME:/opt/app/config $IMAGE_NAME:${CI_COMMIT_TAG}
|
||||
|
||||
|
@ -6,10 +6,6 @@ LABEL ImageName="registry.hottis.de/hv2/hv2-api"
|
||||
ARG APP_DIR="/opt/app"
|
||||
ARG CONF_DIR="${APP_DIR}/config"
|
||||
|
||||
ENV DB_HOST="172.16.10.18"
|
||||
ENV DB_NAME="hausverwaltung"
|
||||
ENV DB_USER="hausverwaltung-ui"
|
||||
ENV DB_PASS="test123"
|
||||
|
||||
|
||||
RUN \
|
||||
|
8
ENV.tmpl
8
ENV.tmpl
@ -1,2 +1,10 @@
|
||||
# copy to ENV and adjust values
|
||||
|
||||
JWT_PUB_KEY="..."
|
||||
|
||||
DB_USER="hv2"
|
||||
DB_PASS="..."
|
||||
DB_HOST="172.16.10.27"
|
||||
DB_NAME="hv2"
|
||||
|
||||
|
||||
|
8
account.py
Normal file
8
account.py
Normal file
@ -0,0 +1,8 @@
|
||||
from db import dbGetMany, dbGetOne
|
||||
|
||||
def getAccounts(user, token_info):
|
||||
return dbGetMany(user, token_info, {"statement": "SELECT id, description FROM account_t", "params": ()})
|
||||
|
||||
def getAccount(user, token_info, accountId=None):
|
||||
return dbGetOne(user, token_info, {"statement": "select id, description from account_t where id = %s", "params": (accountId, )})
|
||||
|
12
auth.py
12
auth.py
@ -2,7 +2,7 @@ from jose import JWTError, jwt
|
||||
import werkzeug
|
||||
import os
|
||||
from loguru import logger
|
||||
|
||||
import json
|
||||
|
||||
JWT_PUB_KEY = ""
|
||||
try:
|
||||
@ -19,9 +19,9 @@ def decodeToken(token):
|
||||
logger.error("{}".format(e))
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
|
||||
def testToken(user, token_info):
|
||||
return '''
|
||||
You are user_id {user} and the provided token has been signed by this issuers. Fine.'.
|
||||
Decoded token claims: {token_info}.
|
||||
'''.format(user=user, token_info=token_info)
|
||||
|
||||
def testToken(user, token_info):
|
||||
return {
|
||||
"message": f"You are user_id {user} and the provided token has been signed by this issuers. Fine.",
|
||||
"details": json.dumps(token_info)
|
||||
}
|
103
db.py
Normal file
103
db.py
Normal file
@ -0,0 +1,103 @@
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
from loguru import logger
|
||||
import os
|
||||
import configparser
|
||||
import json
|
||||
import werkzeug
|
||||
|
||||
|
||||
|
||||
DB_USER = ""
|
||||
DB_PASS = ""
|
||||
DB_HOST = ""
|
||||
DB_NAME = ""
|
||||
try:
|
||||
DB_USER = os.environ["DB_USER"]
|
||||
DB_PASS = os.environ["DB_PASS"]
|
||||
DB_HOST = os.environ["DB_HOST"]
|
||||
DB_NAME = os.environ["DB_NAME"]
|
||||
except KeyError:
|
||||
config = configparser.ConfigParser()
|
||||
config.read('/opt/app/config/dbconfig.ini')
|
||||
DB_USER = config["database"]["user"]
|
||||
DB_PASS = config["database"]["pass"]
|
||||
DB_HOST = config["database"]["host"]
|
||||
DB_NAME = config["database"]["name"]
|
||||
|
||||
|
||||
|
||||
class NoDataFoundException(Exception): pass
|
||||
|
||||
class TooMuchDataFoundException(Exception): pass
|
||||
|
||||
|
||||
def databaseOperation(cursor, params):
|
||||
cursor.execute('SELECT key, value FROM claims_for_user_v where "user" = %s and application = %s',
|
||||
params)
|
||||
for claimObj in cursor:
|
||||
logger.debug("add claim {} -> {}".format(claimObj[0], claimObj[1]))
|
||||
return []
|
||||
|
||||
|
||||
def execDatabaseOperation(func, params):
|
||||
conn = None
|
||||
cur = None
|
||||
try:
|
||||
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
|
||||
host = DB_HOST, database = DB_NAME,
|
||||
sslmode = 'require')
|
||||
conn.autocommit = False
|
||||
|
||||
with conn.cursor(cursor_factory = psycopg2.extras.RealDictCursor) as cur:
|
||||
return func(cur, params)
|
||||
except psycopg2.Error as err:
|
||||
raise Exception("Error when connecting to database: {}".format(err))
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
def _opGetMany(cursor, params):
|
||||
items = []
|
||||
cursor.execute(params["statement"])
|
||||
for itemObj in cursor:
|
||||
logger.debug("item received {}".format(str(itemObj)))
|
||||
items.append(itemObj)
|
||||
return items
|
||||
|
||||
def dbGetMany(user, token_info, params):
|
||||
logger.info("params: {}, token: {}".format(params, json.dumps(token_info)))
|
||||
try:
|
||||
return execDatabaseOperation(_opGetMany, params)
|
||||
except Exception as e:
|
||||
logger.error(f"Exception: {e}")
|
||||
raise werkzeug.exceptions.InternalServerError
|
||||
|
||||
|
||||
|
||||
def _opGetOne(cursor, params):
|
||||
cursor.execute(params["statement"], params["params"])
|
||||
itemObj = cursor.fetchone()
|
||||
logger.debug(f"item received: {itemObj}")
|
||||
if not itemObj:
|
||||
raise NoDataFoundException
|
||||
dummyObj = cursor.fetchone()
|
||||
if dummyObj:
|
||||
raise TooMuchDataFoundException
|
||||
return itemObj
|
||||
|
||||
def dbGetOne(user, token_info, params):
|
||||
logger.info("params: {}, token: {}".format(params, json.dumps(token_info)))
|
||||
try:
|
||||
return execDatabaseOperation(_opGetOne, params)
|
||||
except NoDataFoundException:
|
||||
logger.error("no data found")
|
||||
raise werkzeug.exceptions.NotFound
|
||||
except TooMuchDataFoundException:
|
||||
logger.error("too much data found")
|
||||
raise werkzeug.exceptions.InternalServerError
|
||||
except Exception as e:
|
||||
logger.error(f"Exception: {e}")
|
||||
raise werkzeug.exceptions.InternalServerError
|
282
openapi.yaml
282
openapi.yaml
@ -2,16 +2,14 @@ openapi: 3.0.0
|
||||
info:
|
||||
title: hv2-api
|
||||
version: "1"
|
||||
description: "REST-API for the nober.de Hausverwaltungs-Software"
|
||||
description: "REST-API for the Nober Grundbesitz GbR Hausverwaltungs-Software"
|
||||
termsOfService: "https://home.hottis.de/dokuwiki/doku.php?id=hv2pub:termsofuse"
|
||||
contact:
|
||||
name: "Wolfgang Hottgenroth"
|
||||
email: "wolfgang.hottgenroth@icloud.com"
|
||||
externalDocs:
|
||||
description: "Find more details on this API here"
|
||||
description: "Find more details here"
|
||||
url: "https://home.hottis.de/dokuwiki/doku.php?id=hv2pub:externaldocs"
|
||||
server:
|
||||
url: "https://api.hv.nober.de/v1"
|
||||
|
||||
paths:
|
||||
/v1/test:
|
||||
@ -23,12 +21,201 @@ paths:
|
||||
'200':
|
||||
description: secret response
|
||||
content:
|
||||
'text/plain':
|
||||
'application/json':
|
||||
schema:
|
||||
type: string
|
||||
$ref: '#/components/schemas/TestOutput'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/accounts:
|
||||
get:
|
||||
tags: [ "Account" ]
|
||||
summary: Return all normalized accounts
|
||||
operationId: account.getAccounts
|
||||
responses:
|
||||
'200':
|
||||
description: accounts response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Account'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/accounts/{accountId}:
|
||||
get:
|
||||
tags: [ "Account" ]
|
||||
summary: Return the normalized account with given id
|
||||
operationId: account.getAccount
|
||||
parameters:
|
||||
- name: accountId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: accounts response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Account'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenants:
|
||||
get:
|
||||
tags: [ "Tenant" ]
|
||||
summary: Return all normalized tenants
|
||||
operationId: tenant.getTenants
|
||||
responses:
|
||||
'200':
|
||||
description: tenant response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenants/{tenantId}:
|
||||
get:
|
||||
tags: [ "Tenant" ]
|
||||
summary: Return the normalized tenant with given id
|
||||
operationId: tenant.getTenant
|
||||
parameters:
|
||||
- name: tenantId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: tenants response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Tenant'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/commercialPremises:
|
||||
get:
|
||||
tags: [ "CommercialPremise" ]
|
||||
summary: Return all normalized commercial premises
|
||||
operationId: rentalObjects.getCommercialPremises
|
||||
responses:
|
||||
'200':
|
||||
description: commercial premise response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/CommercialPremise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/commercialPremises/{commercialPremiseId}:
|
||||
get:
|
||||
tags: [ "CommercialPremise" ]
|
||||
summary: Return the normalized commercial premise with given id
|
||||
operationId: rentalObjects.getCommercialPremise
|
||||
parameters:
|
||||
- name: commercialPremiseId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: commercial premise response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/CommercialPremise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/parkings:
|
||||
get:
|
||||
tags: [ "Parking" ]
|
||||
summary: Return all normalized parkings
|
||||
operationId: rentalObjects.getParkings
|
||||
responses:
|
||||
'200':
|
||||
description: parkings response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Parking'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/parkings/{parkingId}:
|
||||
get:
|
||||
tags: [ "Parking" ]
|
||||
summary: Return the normalized parking with given id
|
||||
operationId: rentalObjects.getParking
|
||||
parameters:
|
||||
- name: parkingId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: parking response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Parking'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/flats:
|
||||
get:
|
||||
tags: [ "Flat" ]
|
||||
summary: Return all normalized flats
|
||||
operationId: rentalObjects.getFlats
|
||||
responses:
|
||||
'200':
|
||||
description: flat response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Flat'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/flats/{flatId}:
|
||||
get:
|
||||
tags: [ "Flat" ]
|
||||
summary: Return the normalized flat with given id
|
||||
operationId: rentalObjects.getFlat
|
||||
parameters:
|
||||
- name: flatId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: flat response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Flat'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
@ -37,3 +224,84 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
x-bearerInfoFunc: auth.decodeToken
|
||||
schemas:
|
||||
TestOutput:
|
||||
description: Test Output
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
details:
|
||||
type: string
|
||||
Account:
|
||||
description: Account
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
Tenant:
|
||||
description: Tenant
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
salutation:
|
||||
type: string
|
||||
firstname:
|
||||
type: string
|
||||
lastname:
|
||||
type: string
|
||||
address1:
|
||||
type: string
|
||||
address2:
|
||||
type: string
|
||||
address3:
|
||||
type: string
|
||||
zip:
|
||||
type: string
|
||||
city:
|
||||
type: string
|
||||
phone1:
|
||||
type: string
|
||||
phone2:
|
||||
type: string
|
||||
iban:
|
||||
type: string
|
||||
account:
|
||||
type: integer
|
||||
CommercialPremise:
|
||||
description: CommercialPremise
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
Parking:
|
||||
description: Parking
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
Flat:
|
||||
description: Flat
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
area:
|
||||
type: number
|
||||
flat_no:
|
||||
type: integer
|
||||
|
71
rentalObjects.py
Normal file
71
rentalObjects.py
Normal file
@ -0,0 +1,71 @@
|
||||
from db import dbGetMany, dbGetOne
|
||||
|
||||
def getCommercialPremises(user, token_info):
|
||||
return dbGetMany(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise
|
||||
FROM commercial_premise_t
|
||||
""",
|
||||
"params": ()
|
||||
}
|
||||
)
|
||||
|
||||
def getCommercialPremise(user, token_info, commercialPremiseId=None):
|
||||
return dbGetOne(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise
|
||||
FROM commercial_premise_t
|
||||
WHERE id = %s
|
||||
""",
|
||||
"params": (commercialPremiseId, )
|
||||
}
|
||||
)
|
||||
|
||||
def getFlats(user, token_info):
|
||||
return dbGetMany(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise, area, flat_no
|
||||
FROM flat_t
|
||||
""",
|
||||
"params": ()
|
||||
}
|
||||
)
|
||||
|
||||
def getFlat(user, token_info, flatId=None):
|
||||
return dbGetOne(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise, area, flat_no
|
||||
FROM flat_t
|
||||
WHERE id = %s
|
||||
""",
|
||||
"params": (flatId, )
|
||||
}
|
||||
)
|
||||
|
||||
def getParking(user, token_info):
|
||||
return dbGetMany(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise
|
||||
FROM parking_t
|
||||
""",
|
||||
"params": ()
|
||||
}
|
||||
)
|
||||
|
||||
def getParking(user, token_info, parkingId=None):
|
||||
return dbGetOne(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, description, premise
|
||||
FROM parking_t
|
||||
WHERE id = %s
|
||||
""",
|
||||
"params": (parkingId, )
|
||||
}
|
||||
)
|
||||
|
29
tenant.py
Normal file
29
tenant.py
Normal file
@ -0,0 +1,29 @@
|
||||
from db import dbGetMany, dbGetOne
|
||||
|
||||
def getTenants(user, token_info):
|
||||
return dbGetMany(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, salutation, firstname, lastname,
|
||||
address1, address2, address3, zip, city,
|
||||
phone1, phone2, iban, account
|
||||
FROM tenant_t
|
||||
""",
|
||||
"params": ()
|
||||
}
|
||||
)
|
||||
|
||||
def getTenant(user, token_info, tenantId=None):
|
||||
return dbGetOne(user, token_info,
|
||||
{
|
||||
"statement": """
|
||||
SELECT id, salutation, firstname, lastname,
|
||||
address1, address2, address3, zip, city,
|
||||
phone1, phone2, iban, account
|
||||
FROM tenant_t
|
||||
WHERE id = %s
|
||||
""",
|
||||
"params": (tenantId, )
|
||||
}
|
||||
)
|
||||
|
Reference in New Issue
Block a user