Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
81b900374e
|
|||
9cabfdd484
|
|||
7b539fc81f
|
|||
284eb77bfe
|
|||
b6c33534f1
|
|||
67c97ed4bc
|
|||
81ba2655b3
|
|||
1e9d22483c
|
|||
daca7eca9b
|
|||
32a66dc189
|
|||
3dd00d6dcd
|
|||
44b58bc48c
|
|||
1bddff4a23
|
|||
b500b81d80
|
|||
5937c99eb4
|
|||
dfc5c8421a
|
|||
b30e587c34
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,2 +1,7 @@
|
||||
__pycache__/
|
||||
ENV
|
||||
ENV
|
||||
|
||||
# generated files
|
||||
create.sql
|
||||
methods.py
|
||||
openapi.yaml
|
||||
|
@ -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}
|
||||
|
||||
|
17
Dockerfile
17
Dockerfile
@ -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 \
|
||||
@ -22,7 +18,8 @@ RUN \
|
||||
pip3 install uwsgi && \
|
||||
pip3 install flask-cors && \
|
||||
pip3 install python-jose[cryptography] && \
|
||||
pip3 install loguru
|
||||
pip3 install loguru && \
|
||||
pip3 install Cheetah3
|
||||
|
||||
|
||||
|
||||
@ -32,13 +29,19 @@ RUN \
|
||||
useradd -d ${APP_DIR} -u 1000 user
|
||||
|
||||
COPY *.py ${APP_DIR}/
|
||||
COPY openapi.yaml ${APP_DIR}/
|
||||
COPY openapi.yaml.tmpl ${APP_DIR}/
|
||||
COPY methods.py.tmpl ${APP_DIR}/
|
||||
COPY schema.json ${APP_DIR}/
|
||||
COPY server.ini ${CONF_DIR}/
|
||||
|
||||
USER 1000:1000
|
||||
WORKDIR ${APP_DIR}
|
||||
VOLUME ${CONF_DIR}
|
||||
|
||||
RUN \
|
||||
python3 generate.py
|
||||
|
||||
USER 1000:1000
|
||||
|
||||
EXPOSE 5000
|
||||
EXPOSE 9191
|
||||
|
||||
|
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"
|
||||
|
||||
|
||||
|
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)
|
||||
}
|
35
create.sql.tmpl
Normal file
35
create.sql.tmpl
Normal file
@ -0,0 +1,35 @@
|
||||
-- -----------------------------------------------------
|
||||
-- THIS IS GENERATED CODE, DO NOT EDIT MANUALLY!
|
||||
-- ALL CHANGES WILL BE LOST AFTER THE NEXT RUN
|
||||
-- OF generate.py
|
||||
-- -----------------------------------------------------
|
||||
|
||||
#for $table in $tables
|
||||
CREATE TABLE ${table.name}_t (
|
||||
id serial not null primary key
|
||||
#for $column in $table.columns
|
||||
,$column.name $column.sqltype #slurp
|
||||
#if (('notnull' in $column) and $column.notnull)
|
||||
not null #slurp
|
||||
#end if
|
||||
#if (('primarykey' in $column) and $column.primarykey)
|
||||
primary key #slurp
|
||||
#end if
|
||||
#if (('foreignkey' in $column) and $column.foreignkey)
|
||||
references ${column.name}_t (id) #slurp
|
||||
#end if
|
||||
#if ('default' in $column)
|
||||
default $column.default #slurp
|
||||
#end if
|
||||
|
||||
#end for
|
||||
#if ('tableConstraints' in $table)
|
||||
#for $tableConstraint in $table.tableConstraints
|
||||
,$tableConstraint
|
||||
#end for
|
||||
#end if
|
||||
);
|
||||
|
||||
#end for
|
||||
|
||||
|
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
|
38
generate.py
Normal file
38
generate.py
Normal file
@ -0,0 +1,38 @@
|
||||
import json
|
||||
from Cheetah.Template import Template
|
||||
import glob
|
||||
|
||||
with open("schema.json") as schemaFile:
|
||||
schema = json.load(schemaFile)
|
||||
|
||||
|
||||
for table in schema["tables"]:
|
||||
for column in table["columns"]:
|
||||
if column["sqltype"] == 'serial':
|
||||
column["apitype"] = 'integer'
|
||||
column["jstype"] = 'number'
|
||||
elif column["sqltype"] == 'integer':
|
||||
column["apitype"] = 'integer'
|
||||
column["jstype"] = 'number'
|
||||
elif column["sqltype"] == 'date':
|
||||
column["apitype"] = 'string'
|
||||
column["jstype"] = 'string'
|
||||
elif column["sqltype"] == 'timestamp':
|
||||
column["apitype"] = 'string'
|
||||
column["jstype"] = 'string'
|
||||
elif column["sqltype"].startswith('varchar'):
|
||||
column["apitype"] = 'string'
|
||||
column["jstype"] = 'string'
|
||||
elif column["sqltype"].startswith('numeric'):
|
||||
column["apitype"] = 'number'
|
||||
column["jstype"] = 'number'
|
||||
|
||||
|
||||
|
||||
|
||||
for f in glob.glob("*.tmpl"):
|
||||
tmpl = Template(file=f, searchList=[schema])
|
||||
with open(f[:-5], 'w') as outFile:
|
||||
outFile.write(str(tmpl))
|
||||
|
||||
|
39
methods.py.tmpl
Normal file
39
methods.py.tmpl
Normal file
@ -0,0 +1,39 @@
|
||||
#####################################################
|
||||
### THIS IS GENERATED CODE, DO NOT EDIT MANUALLY! ###
|
||||
### ALL CHANGES WILL BE LOST AFTER THE NEXT RUN ###
|
||||
### OF generate.py ###
|
||||
#####################################################
|
||||
|
||||
|
||||
from db import dbGetMany, dbGetOne
|
||||
|
||||
#for $table in $tables
|
||||
def get_${table.name}s(user, token_info):
|
||||
return dbGetMany(user, token_info, {
|
||||
"statement": """
|
||||
SELECT
|
||||
id
|
||||
#for $column in $table.columns
|
||||
,$column.name
|
||||
#end for
|
||||
FROM ${table.name}_t
|
||||
""",
|
||||
"params": ()
|
||||
}
|
||||
)
|
||||
|
||||
def get_${table.name}(user, token_info, ${table.name}Id=None):
|
||||
return dbGetOne(user, token_info, {
|
||||
"statement": """
|
||||
SELECT
|
||||
id
|
||||
#for $column in $table.columns
|
||||
,$column.name
|
||||
#end for
|
||||
FROM ${table.name}_t
|
||||
WHERE id = %s
|
||||
""",
|
||||
"params": (${table.name}Id, )
|
||||
}
|
||||
)
|
||||
#end for
|
631
openapi.yaml
631
openapi.yaml
@ -1,3 +1,6 @@
|
||||
|
||||
|
||||
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: hv2-api
|
||||
@ -12,21 +15,462 @@ externalDocs:
|
||||
url: "https://home.hottis.de/dokuwiki/doku.php?id=hv2pub:externaldocs"
|
||||
|
||||
paths:
|
||||
/v1/test:
|
||||
/v1/accounts:
|
||||
get:
|
||||
tags: [ "Test" ]
|
||||
summary: Return secret string
|
||||
operationId: auth.testToken
|
||||
tags: [ "account" ]
|
||||
summary: Return all normalized accounts
|
||||
operationId: methods.get_accounts
|
||||
responses:
|
||||
'200':
|
||||
description: secret response
|
||||
description: accounts response
|
||||
content:
|
||||
'text/plain':
|
||||
'application/json':
|
||||
schema:
|
||||
type: string
|
||||
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: methods.get_account
|
||||
parameters:
|
||||
- name: accountId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: account 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: methods.get_tenants
|
||||
responses:
|
||||
'200':
|
||||
description: tenants 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: methods.get_tenant
|
||||
parameters:
|
||||
- name: tenantId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: tenant response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/tenant'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/premises:
|
||||
get:
|
||||
tags: [ "premise" ]
|
||||
summary: Return all normalized premises
|
||||
operationId: methods.get_premises
|
||||
responses:
|
||||
'200':
|
||||
description: premises response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/premise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/premises/{premiseId}:
|
||||
get:
|
||||
tags: [ "premise" ]
|
||||
summary: Return the normalized premise with given id
|
||||
operationId: methods.get_premise
|
||||
parameters:
|
||||
- name: premiseId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: premise response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/premise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/flats:
|
||||
get:
|
||||
tags: [ "flat" ]
|
||||
summary: Return all normalized flats
|
||||
operationId: methods.get_flats
|
||||
responses:
|
||||
'200':
|
||||
description: flats 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: methods.get_flat
|
||||
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']
|
||||
/v1/overhead_advances:
|
||||
get:
|
||||
tags: [ "overhead_advance" ]
|
||||
summary: Return all normalized overhead_advances
|
||||
operationId: methods.get_overhead_advances
|
||||
responses:
|
||||
'200':
|
||||
description: overhead_advances response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/overhead_advance'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/overhead_advances/{overhead_advanceId}:
|
||||
get:
|
||||
tags: [ "overhead_advance" ]
|
||||
summary: Return the normalized overhead_advance with given id
|
||||
operationId: methods.get_overhead_advance
|
||||
parameters:
|
||||
- name: overhead_advanceId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: overhead_advance response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/overhead_advance'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/overhead_advance_flat_mappings:
|
||||
get:
|
||||
tags: [ "overhead_advance_flat_mapping" ]
|
||||
summary: Return all normalized overhead_advance_flat_mappings
|
||||
operationId: methods.get_overhead_advance_flat_mappings
|
||||
responses:
|
||||
'200':
|
||||
description: overhead_advance_flat_mappings response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/overhead_advance_flat_mapping'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/overhead_advance_flat_mappings/{overhead_advance_flat_mappingId}:
|
||||
get:
|
||||
tags: [ "overhead_advance_flat_mapping" ]
|
||||
summary: Return the normalized overhead_advance_flat_mapping with given id
|
||||
operationId: methods.get_overhead_advance_flat_mapping
|
||||
parameters:
|
||||
- name: overhead_advance_flat_mappingId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: overhead_advance_flat_mapping response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/overhead_advance_flat_mapping'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/parkings:
|
||||
get:
|
||||
tags: [ "parking" ]
|
||||
summary: Return all normalized parkings
|
||||
operationId: methods.get_parkings
|
||||
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: methods.get_parking
|
||||
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/commercial_premises:
|
||||
get:
|
||||
tags: [ "commercial_premise" ]
|
||||
summary: Return all normalized commercial_premises
|
||||
operationId: methods.get_commercial_premises
|
||||
responses:
|
||||
'200':
|
||||
description: commercial_premises response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/commercial_premise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/commercial_premises/{commercial_premiseId}:
|
||||
get:
|
||||
tags: [ "commercial_premise" ]
|
||||
summary: Return the normalized commercial_premise with given id
|
||||
operationId: methods.get_commercial_premise
|
||||
parameters:
|
||||
- name: commercial_premiseId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: commercial_premise response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/commercial_premise'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenancys:
|
||||
get:
|
||||
tags: [ "tenancy" ]
|
||||
summary: Return all normalized tenancys
|
||||
operationId: methods.get_tenancys
|
||||
responses:
|
||||
'200':
|
||||
description: tenancys response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/tenancy'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenancys/{tenancyId}:
|
||||
get:
|
||||
tags: [ "tenancy" ]
|
||||
summary: Return the normalized tenancy with given id
|
||||
operationId: methods.get_tenancy
|
||||
parameters:
|
||||
- name: tenancyId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: tenancy response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/tenancy'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/fees:
|
||||
get:
|
||||
tags: [ "fee" ]
|
||||
summary: Return all normalized fees
|
||||
operationId: methods.get_fees
|
||||
responses:
|
||||
'200':
|
||||
description: fees response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/fee'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/fees/{feeId}:
|
||||
get:
|
||||
tags: [ "fee" ]
|
||||
summary: Return the normalized fee with given id
|
||||
operationId: methods.get_fee
|
||||
parameters:
|
||||
- name: feeId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: fee response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/fee'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenancy_fee_mappings:
|
||||
get:
|
||||
tags: [ "tenancy_fee_mapping" ]
|
||||
summary: Return all normalized tenancy_fee_mappings
|
||||
operationId: methods.get_tenancy_fee_mappings
|
||||
responses:
|
||||
'200':
|
||||
description: tenancy_fee_mappings response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/tenancy_fee_mapping'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/tenancy_fee_mappings/{tenancy_fee_mappingId}:
|
||||
get:
|
||||
tags: [ "tenancy_fee_mapping" ]
|
||||
summary: Return the normalized tenancy_fee_mapping with given id
|
||||
operationId: methods.get_tenancy_fee_mapping
|
||||
parameters:
|
||||
- name: tenancy_fee_mappingId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: tenancy_fee_mapping response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/tenancy_fee_mapping'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/account_entrys:
|
||||
get:
|
||||
tags: [ "account_entry" ]
|
||||
summary: Return all normalized account_entrys
|
||||
operationId: methods.get_account_entrys
|
||||
responses:
|
||||
'200':
|
||||
description: account_entrys response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/account_entry'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/account_entrys/{account_entryId}:
|
||||
get:
|
||||
tags: [ "account_entry" ]
|
||||
summary: Return the normalized account_entry with given id
|
||||
operationId: methods.get_account_entry
|
||||
parameters:
|
||||
- name: account_entryId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: account_entry response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/account_entry'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
@ -35,3 +479,174 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
x-bearerInfoFunc: auth.decodeToken
|
||||
schemas:
|
||||
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
|
||||
premise:
|
||||
description: premise
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
street:
|
||||
type: string
|
||||
zip:
|
||||
type: string
|
||||
city:
|
||||
type: string
|
||||
flat:
|
||||
description: flat
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
area:
|
||||
type: number
|
||||
flat_no:
|
||||
type: integer
|
||||
overhead_advance:
|
||||
description: overhead_advance
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
amount:
|
||||
type: number
|
||||
startdate:
|
||||
type: string
|
||||
enddate:
|
||||
type: string
|
||||
overhead_advance_flat_mapping:
|
||||
description: overhead_advance_flat_mapping
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
overhead_advance:
|
||||
type: integer
|
||||
flat:
|
||||
type: integer
|
||||
parking:
|
||||
description: parking
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
commercial_premise:
|
||||
description: commercial_premise
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
premise:
|
||||
type: integer
|
||||
tenancy:
|
||||
description: tenancy
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
tenant:
|
||||
type: integer
|
||||
flat:
|
||||
type: integer
|
||||
parking:
|
||||
type: integer
|
||||
commercial_premise:
|
||||
type: integer
|
||||
startdate:
|
||||
type: string
|
||||
enddate:
|
||||
type: string
|
||||
fee:
|
||||
description: fee
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
amount:
|
||||
type: number
|
||||
fee_type:
|
||||
type: string
|
||||
startdate:
|
||||
type: string
|
||||
enddate:
|
||||
type: string
|
||||
tenancy_fee_mapping:
|
||||
description: tenancy_fee_mapping
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
tenancy:
|
||||
type: integer
|
||||
fee:
|
||||
type: integer
|
||||
account_entry:
|
||||
description: account_entry
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description:
|
||||
type: string
|
||||
account:
|
||||
type: integer
|
||||
created_at:
|
||||
type: string
|
||||
amount:
|
||||
type: number
|
||||
|
83
openapi.yaml.tmpl
Normal file
83
openapi.yaml.tmpl
Normal file
@ -0,0 +1,83 @@
|
||||
#####################################################
|
||||
### THIS IS GENERATED CODE, DO NOT EDIT MANUALLY! ###
|
||||
### ALL CHANGES WILL BE LOST AFTER THE NEXT RUN ###
|
||||
### OF generate.py ###
|
||||
#####################################################
|
||||
|
||||
|
||||
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: hv2-api
|
||||
version: "1"
|
||||
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 here"
|
||||
url: "https://home.hottis.de/dokuwiki/doku.php?id=hv2pub:externaldocs"
|
||||
|
||||
paths:
|
||||
#for $table in $tables
|
||||
/v1/${table.name}s:
|
||||
get:
|
||||
tags: [ "$table.name" ]
|
||||
summary: Return all normalized ${table.name}s
|
||||
operationId: methods.get_${table.name}s
|
||||
responses:
|
||||
'200':
|
||||
description: ${table.name}s response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
\$ref: '#/components/schemas/$table.name'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
/v1/${table.name}s/{${table.name}Id}:
|
||||
get:
|
||||
tags: [ "$table.name" ]
|
||||
summary: Return the normalized $table.name with given id
|
||||
operationId: methods.get_$table.name
|
||||
parameters:
|
||||
- name: ${table.name}Id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: $table.name response
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
\$ref: '#/components/schemas/$table.name'
|
||||
security:
|
||||
- jwt: ['secret']
|
||||
#end for
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
jwt:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
x-bearerInfoFunc: auth.decodeToken
|
||||
schemas:
|
||||
#for $table in $tables
|
||||
$table.name:
|
||||
description: $table.name
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
#for $column in $table.columns
|
||||
$column.name:
|
||||
type: $column.apitype
|
||||
#end for
|
||||
#end for
|
119
schema.json
Normal file
119
schema.json
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"tables": [
|
||||
{
|
||||
"name": "account",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)", "notnull": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tenant",
|
||||
"columns": [
|
||||
{ "name": "salutation", "sqltype": "varchar(128)" },
|
||||
{ "name": "firstname", "sqltype": "varchar(128)" },
|
||||
{ "name": "lastname", "sqltype": "varchar(128)" },
|
||||
{ "name": "address1", "sqltype": "varchar(128)" },
|
||||
{ "name": "address2", "sqltype": "varchar(128)" },
|
||||
{ "name": "address3", "sqltype": "varchar(128)" },
|
||||
{ "name": "zip", "sqltype": "varchar(10)" },
|
||||
{ "name": "city", "sqltype": "varchar(128)" },
|
||||
{ "name": "phone1", "sqltype": "varchar(64)" },
|
||||
{ "name": "phone2", "sqltype": "varchar(64)" },
|
||||
{ "name": "iban", "sqltype": "varchar(64)" },
|
||||
{ "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "premise",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "street", "sqltype": "varchar(128)", "notnull": true },
|
||||
{ "name": "zip", "sqltype": "varchar(10)", "notnull": true },
|
||||
{ "name": "city", "sqltype": "varchar(128)", "notnull": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flat",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "premise", "sqltype": "integer", "foreignkey": true },
|
||||
{ "name": "area", "sqltype": "numeric(10,2)", "notnull": true },
|
||||
{ "name": "flat_no", "sqltype": "integer" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "overhead_advance",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "amount", "sqltype": "numeric(10,4)", "notnull": true },
|
||||
{ "name": "startdate", "sqltype": "date" },
|
||||
{ "name": "enddate", "sqltype": "date" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "overhead_advance_flat_mapping",
|
||||
"columns": [
|
||||
{ "name": "overhead_advance", "sqltype": "integer", "notnull": true, "foreignkey": true },
|
||||
{ "name": "flat", "sqltype": "integer", "notnull": true, "foreignkey": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "parking",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "premise", "sqltype": "integer", "foreignkey": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "commercial_premise",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "premise", "sqltype": "integer", "foreignkey": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tenancy",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "tenant", "sqltype": "integer", "notnull": true, "foreignkey": true },
|
||||
{ "name": "flat", "sqltype": "integer", "notnull": false, "foreignkey": true },
|
||||
{ "name": "parking", "sqltype": "integer", "notnull": false, "foreignkey": true },
|
||||
{ "name": "commercial_premise", "sqltype": "integer", "notnull": false, "foreignkey": true },
|
||||
{ "name": "startdate", "sqltype": "date", "notnull": true },
|
||||
{ "name": "enddate", "sqltype": "date", "notnull": false }
|
||||
],
|
||||
"tableConstraints": [
|
||||
"constraint tenancy_only_one_object check ((flat is not null and parking is null and commercial_premise is null) or (flat is null and parking is not null and commercial_premise is null) or (flat is null and parking is null and commercial_premise is not null))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fee",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)" },
|
||||
{ "name": "amount", "sqltype": "numeric(10,2)", "notnull": true },
|
||||
{ "name": "fee_type", "sqltype": "varchar(10)", "notnull": true },
|
||||
{ "name": "startdate", "sqltype": "date" },
|
||||
{ "name": "enddate", "sqltype": "date" }
|
||||
],
|
||||
"tableConstraints": [
|
||||
"constraint fee_fee_type check (fee_type = 'per_area' or fee_type = 'total')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tenancy_fee_mapping",
|
||||
"columns": [
|
||||
{ "name": "tenancy", "sqltype": "integer", "notnull": true, "foreignkey": true },
|
||||
{ "name": "fee", "sqltype": "integer", "notnull": true, "foreignkey": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "account_entry",
|
||||
"columns": [
|
||||
{ "name": "description", "sqltype": "varchar(128)", "notnull": true },
|
||||
{ "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true },
|
||||
{ "name": "created_at", "sqltype": "timestamp", "notnull": true, "default": "now()" },
|
||||
{ "name": "amount", "sqltype": "numeric(10,2)", "notnull": true }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
48
testdata/testdata.sql
vendored
Normal file
48
testdata/testdata.sql
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
insert into account_t (description) values ('account testtenant1');
|
||||
|
||||
insert into tenant_t (lastname, account) values (
|
||||
'testtenant1',
|
||||
(select id from account_t where description = 'account testtenant1')
|
||||
);
|
||||
|
||||
insert into premise_t (street, zip, city) values ('Hemsingskotten 38', '45259', 'Essen');
|
||||
|
||||
insert into parking_t (description, premise) values ('Garage 1', (select id from premise_t where street = 'Hemsingskotten 38'));
|
||||
|
||||
insert into flat_t (description, premise, area, flat_no) values('EG links', (select id from premise_t where street = 'Hemsingskotten 38'), 59.0, 1);
|
||||
|
||||
insert into fee_t (description, amount, fee_type, startdate) values ('Altenwohnung Hemsingskotten', 5.23, 'per_area', '2021-01-01');
|
||||
|
||||
insert into fee_t (description, amount, fee_type, startdate) values ('Garage intern', 30.0, 'total', '2021-01-01');
|
||||
|
||||
insert into tenancy_t (tenant, flat, description, startdate) values (
|
||||
(select id from tenant_t where lastname = 'testtenant1'),
|
||||
(select id from flat_t where flat_no = 1),
|
||||
'flat testtenant1',
|
||||
'2021-06-01'
|
||||
);
|
||||
|
||||
insert into tenancy_t (tenant, parking, description, startdate) values (
|
||||
(select id from tenant_t where lastname = 'testtenant1'),
|
||||
(select id from parking_t where description = 'Garage 1'),
|
||||
'parking testtenant1',
|
||||
'2021-06-01'
|
||||
);
|
||||
|
||||
insert into overhead_advance_t (description, amount, startdate) values ('BKV Altenwohnung Hemsingskotten', 0.34, '2021-01-01');
|
||||
|
||||
insert into overhead_advance_flat_mapping_t (overhead_advance, flat) values (
|
||||
(select id from overhead_advance_t where description = 'BKV Altenwohnung Hemsingskotten'),
|
||||
(select id from flat_t where flat_no = 1)
|
||||
);
|
||||
|
||||
insert into tenancy_fee_mapping_t (tenancy, fee) values (
|
||||
(select id from tenancy_t where description = 'flat testtenant1'),
|
||||
(select id from fee_t where description = 'Altenwohnung Hemsingskotten')
|
||||
);
|
||||
|
||||
insert into tenancy_fee_mapping_t (tenancy, fee) values (
|
||||
(select id from tenancy_t where description = 'parking testtenant1'),
|
||||
(select id from fee_t where description = 'Garage intern')
|
||||
);
|
||||
|
Reference in New Issue
Block a user