Compare commits

..

1 Commits

Author SHA1 Message Date
35cafe1780 auth.py: Make usage of exceptions instead of if-else-blocks
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-06-21 16:01:29 +02:00
27 changed files with 230 additions and 785 deletions

View File

@ -5,26 +5,10 @@ name: default
steps: steps:
- name: qa - name: qa
image: registry.wtf-eg.net/ki-backend-builder:1.0.0 image: python:3.8-alpine
commands: commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv install --dev - pipenv install --dev
- pipenv run flake8 - pipenv run flake8
- pipenv run python -m unittest discover ki - pipenv run python -m unittest discover ki
- name: docker-publish
image: plugins/docker
settings:
registry: registry.wtf-eg.net
repo: registry.wtf-eg.net/ki-backend
target: ki-backend
auto_tag: true
username:
from_secret: "docker_username"
password:
from_secret: "docker_password"
when:
branch:
- main
image_pull_secrets:
- dockerconfig

View File

@ -1,4 +1,5 @@
[flake8] [flake8]
max-line-length = 120 max-line-length = 120
extend-exclude = exclude =
.git,
migrations migrations

1
.gitignore vendored
View File

@ -1,2 +1 @@
/.env /.env
*.pyc

View File

@ -1,20 +0,0 @@
- repo: local
hooks:
- id: flake8
name: flake8
entry: flake8
language: system
files: ^.*\.py$
exclude: ^(migrations).*$
- id: yapf
name: yapf
entry: yapf -i
language: system
files: ^.*\.py$
exclude: ^(migrations).*$
- id: unittest
name: unittest
entry: python -m unittest discover ki
language: system
exclude: .*
always_run: true

View File

@ -1,4 +0,0 @@
[style]
based_on_style = pep8
allow_split_before_dict_value = 0
column_limit = 120

View File

@ -1,4 +1,17 @@
FROM registry.wtf-eg.net/ki-backend-builder:1.0.0 as builder FROM python:3.8-alpine as base
ENV PYROOT /pyroot
ENV PYTHONUSERBASE $PYROOT
FROM base as builder
RUN apk add --no-cache \
gcc \
g++ \
musl-dev \
python3-dev && \
pip3 install pipenv
COPY Pipfile* ./ COPY Pipfile* ./
@ -6,7 +19,7 @@ RUN PIP_USER=1 PIP_IGNORE_INSTALLED=1 pipenv install --system --deploy --ignore-
RUN pip3 uninstall --yes pipenv RUN pip3 uninstall --yes pipenv
FROM registry.wtf-eg.net/ki-backend-base:1.0.0 as ki-backend FROM base
# Install six explicitly. Otherwise Python complains about it missing. # Install six explicitly. Otherwise Python complains about it missing.
RUN pip3 install six RUN pip3 install six

View File

@ -11,12 +11,9 @@ flask-sqlalchemy = "~=2.5.1"
sqlalchemy = "~=1.4.18" sqlalchemy = "~=1.4.18"
waitress = "~=2.0.0" waitress = "~=2.0.0"
pyyaml = "~=5.4.1" pyyaml = "~=5.4.1"
flask-cors = "~=3.0.10"
[dev-packages] [dev-packages]
flake8 = "~=3.9.2" flake8 = "~=3.9.2"
yapf = "~=0.31.0"
pre-commit = "~=2.13.0"
[requires] [requires]
python_version = "3.8" python_version = "3.8"

135
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "11a821c6c1f072dcf7c39a020056fa289b7a5283aa33d96e2ed6860fbc023fa4" "sha256": "439b60cb87b0180f0b78c531085f9bbeef7685ef038256f80b0a8123e7d144e6"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -21,6 +21,7 @@
"sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51", "sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51",
"sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c" "sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.6.5" "version": "==1.6.5"
}, },
"click": { "click": {
@ -28,6 +29,7 @@
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6" "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
], ],
"markers": "python_version >= '3.6'",
"version": "==8.0.1" "version": "==8.0.1"
}, },
"flask": { "flask": {
@ -38,14 +40,6 @@
"index": "pypi", "index": "pypi",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"flask-cors": {
"hashes": [
"sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438",
"sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"
],
"index": "pypi",
"version": "==3.0.10"
},
"flask-migrate": { "flask-migrate": {
"hashes": [ "hashes": [
"sha256:4d42e8f861d78cb6e9319afcba5bf76062e5efd7784184dd2a1cccd9de34a702", "sha256:4d42e8f861d78cb6e9319afcba5bf76062e5efd7784184dd2a1cccd9de34a702",
@ -122,6 +116,7 @@
"sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c", "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c",
"sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0" "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"jinja2": { "jinja2": {
@ -129,6 +124,7 @@
"sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4",
"sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4" "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.0.1" "version": "==3.0.1"
}, },
"mako": { "mako": {
@ -136,6 +132,7 @@
"sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab", "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab",
"sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e" "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.4" "version": "==1.1.4"
}, },
"markupsafe": { "markupsafe": {
@ -175,6 +172,7 @@
"sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
"sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"python-dateutil": { "python-dateutil": {
@ -182,6 +180,7 @@
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1" "version": "==2.8.1"
}, },
"python-dotenv": { "python-dotenv": {
@ -242,6 +241,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0" "version": "==1.16.0"
}, },
"sqlalchemy": { "sqlalchemy": {
@ -293,38 +293,11 @@
"sha256:1de1db30d010ff1af14a009224ec49ab2329ad2cde454c8a708130642d579c42", "sha256:1de1db30d010ff1af14a009224ec49ab2329ad2cde454c8a708130642d579c42",
"sha256:6c1ec500dcdba0baa27600f6a22f6333d8b662d22027ff9f6202e3367413caa8" "sha256:6c1ec500dcdba0baa27600f6a22f6333d8b662d22027ff9f6202e3367413caa8"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.0.1"
} }
}, },
"develop": { "develop": {
"appdirs": {
"hashes": [
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
"version": "==1.4.4"
},
"cfgv": {
"hashes": [
"sha256:9e600479b3b99e8af981ecdfc80a0296104ee610cab48a5ae4ffd0b668650eb1",
"sha256:b449c9c6118fe8cca7fa5e00b9ec60ba08145d281d52164230a69211c5d597a1"
],
"version": "==3.3.0"
},
"distlib": {
"hashes": [
"sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736",
"sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"
],
"version": "==0.3.2"
},
"filelock": {
"hashes": [
"sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
"sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
],
"version": "==3.0.12"
},
"flake8": { "flake8": {
"hashes": [ "hashes": [
"sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b", "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b",
@ -333,13 +306,6 @@
"index": "pypi", "index": "pypi",
"version": "==3.9.2" "version": "==3.9.2"
}, },
"identify": {
"hashes": [
"sha256:18d0c531ee3dbc112fa6181f34faa179de3f57ea57ae2899754f16a7e0ff6421",
"sha256:5b41f71471bc738e7b586308c3fca172f78940195cb3bf6734c1e66fdac49306"
],
"version": "==2.2.10"
},
"mccabe": { "mccabe": {
"hashes": [ "hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
@ -347,26 +313,12 @@
], ],
"version": "==0.6.1" "version": "==0.6.1"
}, },
"nodeenv": {
"hashes": [
"sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b",
"sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"
],
"version": "==1.6.0"
},
"pre-commit": {
"hashes": [
"sha256:764972c60693dc668ba8e86eb29654ec3144501310f7198742a767bec385a378",
"sha256:b679d0fddd5b9d6d98783ae5f10fd0c4c59954f375b70a58cbe1ce9bcf9809a4"
],
"index": "pypi",
"version": "==2.13.0"
},
"pycodestyle": { "pycodestyle": {
"hashes": [ "hashes": [
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.7.0" "version": "==2.7.0"
}, },
"pyflakes": { "pyflakes": {
@ -374,71 +326,8 @@
"sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3",
"sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.3.1" "version": "==2.3.1"
},
"pyyaml": {
"hashes": [
"sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
"sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
"sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
"sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
"sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
"sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
"sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
"sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
"sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
"sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
"sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
"sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
"sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
"sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
"sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
"sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
"sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
"sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
"sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
"sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
"sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
"sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
"sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
"sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
"sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
"sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
"sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
],
"index": "pypi",
"version": "==5.4.1"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.16.0"
},
"toml": {
"hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"version": "==0.10.2"
},
"virtualenv": {
"hashes": [
"sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467",
"sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"
],
"version": "==20.4.7"
},
"yapf": {
"hashes": [
"sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d",
"sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e"
],
"index": "pypi",
"version": "==0.31.0"
} }
} }
} }

View File

@ -9,7 +9,6 @@
- Python 3.8 - Python 3.8
- [Pipenv](https://github.com/pypa/pipenv) - [Pipenv](https://github.com/pypa/pipenv)
### Entwicklungsumgebung aufbauen und starten ### Entwicklungsumgebung aufbauen und starten
Ggf. vorher aufräumen Ggf. vorher aufräumen
@ -22,39 +21,25 @@ rm data/ki.sqlite
cp env.dev .env cp env.dev .env
pipenv install --dev pipenv install --dev
pipenv shell pipenv shell
export FLASK_APP=app.py
flask db upgrade flask db upgrade
flask seed --dev flask seed
flask run flask run
``` ```
http://localhost:5000/ http://localhost:5000/
### pre-commit einrichten ### Tests ausführen
Damit mensch nicht verpeilt kaputten Code Style zu commiten,
kann pre-commit benutzt werden. Einmal im Virtualenv ausführen:
```
pre-commit install
```
### `alembic` Befehle
`alembic` ist über [Flask-Migrate](https://flask-migrate.readthedocs.io/en/latest/index.html) eingebunden.
Es wird über `flask db ...` aufgerufen.
### QA
``` ```
python -m unittest discover ki python -m unittest discover ki
```
# Code formatieren
yapf -i --recursive ki/
# Code-Style prüfen ### Linting
```
flake8 flake8
``` ```

14
app.py
View File

@ -1,27 +1,17 @@
import logging
import os import os
from dotenv import load_dotenv, find_dotenv from dotenv import load_dotenv, find_dotenv
from flask import Flask from flask import Flask
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
load_dotenv(find_dotenv()) load_dotenv(find_dotenv())
loglevel = os.getenv("KI_LOGLEVEL", logging.WARNING)
loglevel = int(loglevel)
logging.basicConfig(level=loglevel)
logging.debug("Hello from KI")
app = Flask(__name__) app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("SQLALCHEMY_DATABASE_URI") app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("SQLALCHEMY_DATABASE_URI")
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["KI_DATA_DIR"] = os.path.dirname(__file__) + "/data" app.config["KI_DATA_DIR"] = os.path.dirname(__file__) + "/data"
app.config["KI_AUTH"] = os.getenv("KI_AUTH") app.config["KI_AUTH"] = os.getenv("KI_AUTH")
app.config["CORS_ORIGINS"] = os.getenv("CORS_ORIGINS", "*")
CORS(app)
db = SQLAlchemy(app) db = SQLAlchemy(app)
migrate = Migrate(app, db) migrate = Migrate(app, db)

View File

@ -1,5 +1,3 @@
--- ---
peter: peter:
password: geheim password: geheim
klaus:
password: jutta

View File

@ -3,11 +3,3 @@ id,name
2,Vue.js 2,Vue.js
3,Python 3,Python
4,JavaScript 4,JavaScript
5,Angular
6,Flask
7,SQLAlchemy
8,Rust
9,MySQL
10,PostgreSQL
11,SQLite
12,Node.js

1 id name
3 2 Vue.js
4 3 Python
5 4 JavaScript
5 Angular
6 Flask
7 SQLAlchemy
8 Rust
9 MySQL
10 PostgreSQL
11 SQLite
12 Node.js

14
drone.yml Normal file
View File

@ -0,0 +1,14 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: qa
image: python3.8-alpine
commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv install --system
- flake8
- python -m unittest discover ki

10
env.dev
View File

@ -1,11 +1,3 @@
SQLALCHEMY_DATABASE_URI=sqlite:///data/ki.sqlite SQLALCHEMY_DATABASE_URI = 'sqlite:///data/ki.sqlite'
CORS_ORIGINS=*
FLASK_APP=app.py
FLASK_ENV=development
KI_AUTH=file KI_AUTH=file
# 10 = debug
KI_LOGLEVEL=10

View File

@ -1 +0,0 @@
from ki.actions.seed import seed # noqa

View File

@ -1,89 +0,0 @@
import csv
import logging
from app import app, db
from ki.models import Address, Contact, ContactType, Language, Skill, Profile, ProfileLanguage, ProfileSkill, User
def seed(dev: bool):
skill_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/skills.csv"
logging.info("importing skills")
with open(skill_seed_file_path) as skills_file:
skills_csv_reader = csv.DictReader(skills_file)
for skill in skills_csv_reader:
id = int(skill["id"])
db_skill = Skill.query.get(id)
if db_skill is None:
db.session.add(Skill(id=int(skill["id"]), name=skill["name"]))
logging.info("importing languages")
iso_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/iso_639_1.csv"
with open(iso_seed_file_path) as iso_file:
iso_csv_reader = csv.DictReader(iso_file)
for iso in iso_csv_reader:
id = iso["639-1"]
db_language = Language.query.get(id)
if db_language is None:
db.session.add(Language(id=iso["639-1"], name=iso["Sprache"]))
if dev:
logging.info("seeding peter :)")
peter = User(auth_id="peter")
db.session.add(peter)
peters_profile = Profile(nickname="peternichtlustig",
pronouns="Herr Dr. Dr.",
volunteerwork="Gartenverein",
freetext="Ich mag Kaffee",
user=peter)
db.session.add(peters_profile)
matrix_type = ContactType(name="Matrix")
db.session.add(matrix_type)
matrix_contact = Contact(profile=peters_profile, contacttype=matrix_type, content="@peter:wtf-eg.de")
db.session.add(matrix_contact)
email_type = ContactType(name="E-Mail")
db.session.add(email_type)
email_contact = Contact(profile=peters_profile, contacttype=email_type, content="peter@wtf-eg.de")
db.session.add(email_contact)
peters_address = Address(name="Peter Nichtlustig",
street="Waldweg",
house_number="23i",
additional="Hinterhaus",
postcode="13337",
city="Bielefeld",
country="Deutschland",
profile=peters_profile)
db.session.add(peters_address)
peters_python_skill = ProfileSkill(profile=peters_profile, skill_id=3, level=3)
db.session.add(peters_python_skill)
peters_php_skill = ProfileSkill(profile=peters_profile, skill_id=1, level=5)
db.session.add(peters_php_skill)
peter_de = ProfileLanguage(profile=peters_profile, language_id="de", level=5)
db.session.add(peter_de)
peter_fr = ProfileLanguage(profile=peters_profile, language_id="fr", level=3)
db.session.add(peter_fr)
logging.info("seeding klaus :D")
klaus = User(auth_id="klaus")
db.session.add(klaus)
db.session.commit()

View File

@ -5,28 +5,41 @@ from app import app, db
from ki.models import User, Token from ki.models import User, Token
class UserWrongCredentialsException(Exception):
pass
class UserAllreadyLoggedInException(Exception):
pass
def auth(username, password): def auth(username, password):
auth_file_path = app.config["KI_DATA_DIR"] + "/auth.yml" auth_file_path = app.config["KI_DATA_DIR"] + "/auth.yml"
with open(auth_file_path, "r") as auth_file_stream: with open(auth_file_path, "r") as auth_file_stream:
users = yaml.safe_load(auth_file_stream)
if username not in users: try:
users = yaml.safe_load(auth_file_stream)
except yaml.YAMLError:
print('Could not parse auth.yml.')
try:
auth_user = users[username]
if auth_user["password"] != password:
raise UserWrongCredentialsException
except (UserWrongCredentialsException, KeyError):
print('Wrong username/password combination')
return None return None
auth_user = users[username] else:
user = User.query.filter(User.auth_id.__eq__(username)).first()
if auth_user["password"] != password: token = Token(token=str(uuid.uuid4()), user=user)
return None
user = User.query.filter(User.auth_id.__eq__(username)).first() db.session.add(token)
db.session.commit()
if user is None: return token
user = User(auth_id=username)
db.session.add(user)
token = Token(token=str(uuid.uuid4()), user=user)
db.session.add(token)
db.session.commit()
return token

View File

@ -1,10 +1,39 @@
import click import csv
from app import app from ki.models import Language, Skill
from ki.actions import seed from app import app, db
def seed():
skill_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/skills.csv"
print("importing skills")
with open(skill_seed_file_path) as skills_file:
skills_csv_reader = csv.DictReader(skills_file)
for skill in skills_csv_reader:
id = int(skill["id"])
db_skill = Skill.query.get(id)
if db_skill is None:
db.session.add(Skill(id=int(skill["id"]), name=skill["name"]))
iso_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/iso_639_1.csv"
with open(iso_seed_file_path) as iso_file:
iso_csv_reader = csv.DictReader(iso_file)
for iso in iso_csv_reader:
id = iso["639-1"]
db_language = Language.query.get(id)
if db_language is None:
db.session.add(Language(id=iso["639-1"], name=iso["Sprache"]))
db.session.commit()
@app.cli.command("seed") @app.cli.command("seed")
@click.option("--dev", is_flag=True) def seed_command():
def seed_command(dev): seed()
seed(dev)

View File

@ -1 +0,0 @@
from ki.handlers.update_profile import update_profile # noqa

View File

@ -1,118 +0,0 @@
from flask import make_response, request
from sqlalchemy import not_
from ki.models import Address, Contact, ContactType, Language, User, Profile, ProfileLanguage, ProfileSkill, Skill
from app import db
def update_address(profile, address_data):
address = profile.address
if (address is None):
address = Address(profile=profile)
db.session.add(address)
address.name = address_data.get("name", "")
address.street = address_data.get("street", "")
address.house_number = address_data.get("house_number", "")
address.additional = address_data.get("additional", "")
address.postcode = address_data.get("postcode", "")
address.city = address_data.get("city", "")
address.country = address_data.get("country", "")
def update_languages(profile, languages_data):
profile_language_ids = []
for language_data in languages_data:
language_id = language_data["language"]["id"]
language = Language.query.get(language_id)
profile_language = ProfileLanguage.query.filter(ProfileLanguage.profile == profile,
ProfileLanguage.language_id == language_id).first()
if profile_language is None:
profile_language = ProfileLanguage(profile=profile, language=language)
db.session.add(profile_language)
profile_language.level = language_data["level"]
profile_language_ids.append(language_id)
ProfileLanguage.query.filter(ProfileLanguage.profile == profile,
not_(ProfileLanguage.language_id.in_(profile_language_ids))).delete()
def update_skills(profile, skills_data):
profile_skill_ids = []
for skill_data in skills_data:
skill_name = skill_data["skill"]["name"]
skill = Skill.query.filter(Skill.name == skill_name).first()
if (skill is None):
skill = Skill(name=skill_name)
db.session.add(skill)
profile_skill = ProfileSkill.query.filter(ProfileSkill.profile == profile, ProfileSkill.skill == skill).first()
if (profile_skill is None):
profile_skill = ProfileSkill(profile=profile, skill=skill)
db.session.add(profile_skill)
profile_skill.level = skill_data["level"]
profile_skill_ids.append(skill.id)
ProfileSkill.query.filter(ProfileSkill.profile == profile,
not_(ProfileSkill.skill_id.in_(profile_skill_ids))).delete()
def update_contacts(profile, contacts_data):
contact_ids_to_be_deleted = list(map(lambda c: c.id, profile.contacts))
for contact_data in contacts_data:
contacttype_name = contact_data["contacttype"]["name"]
contacttype = ContactType.query.filter(ContactType.name == contacttype_name).first()
if (contacttype is None):
contacttype = ContactType(name=contacttype_name)
db.session.add(contacttype)
if "id" in contact_data:
contact_id = int(contact_data["id"])
contact_ids_to_be_deleted.remove(contact_id)
contact = Contact.query.get(contact_id)
else:
contact = Contact(profile=profile, contacttype=contacttype)
db.session.add(contact)
contact.contacttype_id = contacttype.id
contact.content = contact_data["content"]
Contact.query.filter(Contact.id.in_(contact_ids_to_be_deleted)).delete()
def update_profile(user_id: int):
user = User.query.get(user_id)
if user is None:
return make_response({}, 404)
profile = user.profile
if (profile is None):
profile = Profile(user=user, nickname=user.auth_id)
db.session.add(profile)
profile.pronouns = request.json.get("pronouns", "")
profile.volunteerwork = request.json.get("volunteerwork", "")
profile.freetext = request.json.get("freetext", "")
update_address(profile, request.json.get("address", {}))
update_contacts(profile, request.json.get("contacts", {}))
update_skills(profile, request.json.get("skills", {}))
update_languages(profile, request.json.get("languages", {}))
db.session.commit()
return make_response({"profile": profile.to_dict()})

View File

@ -13,7 +13,7 @@ class User(db.Model):
auth_id = Column(String(50), nullable=False, unique=True) auth_id = Column(String(50), nullable=False, unique=True)
profile_id = Column(Integer, ForeignKey("profile.id"), nullable=True) profile_id = Column(Integer, ForeignKey("profile.id"), nullable=True)
tokens = relationship("Token", back_populates="user") tokens = relationship("Token", uselist=False, back_populates="user")
profile = relationship("Profile", back_populates="user") profile = relationship("Profile", back_populates="user")
def to_dict(self): def to_dict(self):
@ -29,7 +29,10 @@ class Profile(db.Model):
volunteerwork = Column(String(4000), default="") volunteerwork = Column(String(4000), default="")
freetext = Column(String(4000), default="") freetext = Column(String(4000), default="")
created = Column(DateTime, nullable=False, default=datetime.now) created = Column(DateTime, nullable=False, default=datetime.now)
updated = Column(DateTime, onupdate=datetime.now, nullable=False, default=datetime.now) updated = Column(DateTime,
onupdate=datetime.now,
nullable=False,
default=datetime.now)
user = relationship("User", back_populates="profile", uselist=False) user = relationship("User", back_populates="profile", uselist=False)
contacts = relationship("Contact") contacts = relationship("Contact")
@ -39,15 +42,10 @@ class Profile(db.Model):
def to_dict(self): def to_dict(self):
return { return {
"user_id": self.user.id,
"nickname": self.nickname, "nickname": self.nickname,
"pronouns": self.pronouns, "pronouns": self.pronouns,
"volunteerwork": self.volunteerwork, "volunteerwork": self.volunteerwork,
"freetext": self.freetext, "freetext": self.freetext
"address": self.address.to_dict(),
"contacts": list(map(lambda contact: contact.to_dict(), self.contacts)),
"skills": list(map(lambda skill: skill.to_dict(), self.skills)),
"languages": list(map(lambda language: language.to_dict(), self.languages))
} }
@ -60,9 +58,6 @@ class Token(db.Model):
user = relationship("User", back_populates="tokens") user = relationship("User", back_populates="tokens")
def to_dict(self):
return {"user_id": self.user_id, "token": self.token}
class Contact(db.Model): class Contact(db.Model):
__tablename__ = "contact" __tablename__ = "contact"
@ -70,18 +65,12 @@ class Contact(db.Model):
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
profile_id = Column(Integer, ForeignKey("profile.id"), nullable=False) profile_id = Column(Integer, ForeignKey("profile.id"), nullable=False)
profile = relationship("Profile", back_populates="contacts") profile = relationship("Profile", back_populates="contacts")
contacttype_id = Column(Integer, ForeignKey("contacttype.id"), nullable=False) contacttype_id = Column(Integer,
ForeignKey("contacttype.id"),
nullable=False)
contacttype = relationship("ContactType") contacttype = relationship("ContactType")
content = Column(String(200), nullable=False) content = Column(String(200), nullable=False)
def to_dict(self):
return {
"id": self.id,
"profile_id": self.profile_id,
"contacttype": self.contacttype.to_dict(),
"content": self.content
}
class ContactType(db.Model): class ContactType(db.Model):
__tablename__ = "contacttype" __tablename__ = "contacttype"
@ -89,9 +78,6 @@ class ContactType(db.Model):
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
name = Column(String(25), nullable=False) name = Column(String(25), nullable=False)
def to_dict(self):
return {"id": self.id, "name": self.name}
class Address(db.Model): class Address(db.Model):
__tablename__ = "address" __tablename__ = "address"
@ -108,19 +94,6 @@ class Address(db.Model):
profile_id = Column(Integer, ForeignKey("profile.id"), nullable=False) profile_id = Column(Integer, ForeignKey("profile.id"), nullable=False)
profile = relationship("Profile", back_populates="address") profile = relationship("Profile", back_populates="address")
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"street": self.street,
"house_number": self.house_number,
"additional": self.additional,
"postcode": self.postcode,
"city": self.city,
"country": self.country,
"profile_id": self.profile_id
}
class Skill(db.Model): class Skill(db.Model):
__tablename__ = "skill" __tablename__ = "skill"
@ -131,7 +104,7 @@ class Skill(db.Model):
profiles = relationship("ProfileSkill", back_populates="skill") profiles = relationship("ProfileSkill", back_populates="skill")
def to_dict(self): def to_dict(self):
return {"id": self.id, "name": self.name, "icon_url": "/skills/{}/icon".format(self.id)} return {"id": self.id, "name": self.name}
class ProfileSkill(db.Model): class ProfileSkill(db.Model):
@ -144,9 +117,6 @@ class ProfileSkill(db.Model):
profile = relationship("Profile", back_populates="skills") profile = relationship("Profile", back_populates="skills")
skill = relationship("Skill", back_populates="profiles") skill = relationship("Skill", back_populates="profiles")
def to_dict(self):
return {"profile_id": self.profile_id, "skill": self.skill.to_dict(), "level": self.level}
class Language(db.Model): class Language(db.Model):
__tablename__ = "language" __tablename__ = "language"
@ -157,7 +127,7 @@ class Language(db.Model):
profiles = relationship("ProfileLanguage", back_populates="language") profiles = relationship("ProfileLanguage", back_populates="language")
def to_dict(self): def to_dict(self):
return {"id": self.id, "name": self.name, "icon_url": "/languages/{}/icon".format(self.id)} return {"id": self.id, "name": self.name}
class ProfileLanguage(db.Model): class ProfileLanguage(db.Model):
@ -169,6 +139,3 @@ class ProfileLanguage(db.Model):
profile = relationship("Profile", back_populates="languages") profile = relationship("Profile", back_populates="languages")
language = relationship("Language", back_populates="profiles") language = relationship("Language", back_populates="profiles")
def to_dict(self):
return {"profile_id": self.profile_id, "language": self.language.to_dict(), "level": self.level}

View File

@ -1 +1 @@
from ki import models, commands, routes # noqa from ki import models, commands, routes # noqa

View File

@ -3,9 +3,8 @@ from flask import g, make_response, request, send_file
from functools import wraps from functools import wraps
from ki.auth import auth from ki.auth import auth
from ki.handlers import update_profile as update_profile_handler from ki.models import Language, Skill, Token, User, Profile
from ki.models import Language, Skill, Token, User from app import app, db
from app import app
def token_auth(func): def token_auth(func):
@ -100,7 +99,7 @@ def login():
if token is None: if token is None:
return make_response({}, 403) return make_response({}, 403)
return make_response({"token": token.token, "user_id": token.user_id}) return make_response({"token": token.token})
@app.route("/users/<user_id>/profile") @app.route("/users/<user_id>/profile")
@ -116,22 +115,32 @@ def get_user_profile(user_id):
if profile is None: if profile is None:
return make_response({}, 404) return make_response({}, 404)
return make_response({ return make_response({"profile": profile.to_dict()})
"profile": profile.to_dict(),
})
@app.route("/users/<user_id>/profile", methods=["POST"]) @app.route("/users/<user_id>/profile", methods=["POST"])
@token_auth
def update_profile(user_id): def update_profile(user_id):
if g.user.id != int(user_id): user = User.query.filter(User.id == int(user_id)).first()
return make_response({}, 403)
return update_profile_handler(int(user_id)) if user is None:
return make_response({}, 404)
profile = user.profile
if (profile is None):
profile = Profile(user=user, nickname=user.auth_id)
db.session.add(profile)
profile.pronouns = request.json.get("pronouns", "")
profile.volunteerwork = request.json.get("volunteerwork", "")
profile.freetext = request.json.get("freetext", "")
db.session.commit()
return make_response(profile.to_dict(), 200)
@app.route("/skills") @app.route("/skills")
@token_auth
def get_skills(): def get_skills():
return handle_completion_request(Skill, "skills") return handle_completion_request(Skill, "skills")
@ -143,7 +152,6 @@ def get_skill_icon(skill_id):
@app.route("/languages") @app.route("/languages")
@token_auth
def get_languages(): def get_languages():
return handle_completion_request(Language, "languages") return handle_completion_request(Language, "languages")

View File

@ -1,35 +0,0 @@
from alembic import command
import json
import unittest
from app import app, db, migrate
from ki.actions import seed
class ApiTest(unittest.TestCase):
maxDiff = None
def setUp(self):
app.debug = True
app.config["TESTING"] = True
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
self.client = app.test_client()
with app.app_context():
config = migrate.get_config()
command.upgrade(config, "head")
seed(True)
def tearDown(self):
db.drop_all()
db.engine.dispose()
def login(self, username, password):
login_data = {"username": username, "password": password}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json")
self.assertEqual(login_response.status_code, 200)
self.assertIn("token", login_response.json)
return login_response.json

View File

@ -1,26 +0,0 @@
import json
import unittest
from ki.test.ApiTest import ApiTest
class TestLoginEndpoint(ApiTest):
def test_login(self):
response1_data = self.login("peter", "geheim")
response2_data = self.login("peter", "geheim")
self.assertNotEqual(response1_data["token"], response2_data["token"])
def test_login_wrong_credentails(self):
login_data = {"username": "peter", "password": "123456"}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json")
self.assertEqual(login_response.status_code, 403)
def test_login_unknown_user(self):
login_data = {"username": "karl", "password": "123456"}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json")
self.assertEqual(login_response.status_code, 403)
if __name__ == "main":
unittest.main()

View File

@ -1,223 +1,95 @@
from alembic import command
import unittest import unittest
import json import json
from app import app from app import app, db, migrate
from ki.models import User from ki.commands import seed
from ki.test.ApiTest import ApiTest from ki.models import Profile, User
class TestProfileEndpoint(ApiTest): class TestProfileEndpoint(unittest.TestCase):
maxDiff = None def setUp(self):
app.debug = True
app.config["TESTING"] = True
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
self.client = app.test_client()
def test_update_profile_unauthorised(self): with app.app_context():
login_data = {"username": "klaus", "password": "jutta"} config = migrate.get_config()
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json") command.upgrade(config, "head")
seed()
def tearDown(self):
db.engine.dispose()
def test_create_profile(self):
user = User(auth_id="peter")
db.session.add(user)
db.session.commit()
login_data = {"username": "peter", "password": "geheim"}
login_response = self.client.post("/users/login",
data=json.dumps(login_data),
content_type="application/json")
self.assertEqual(login_response.status_code, 200) self.assertEqual(login_response.status_code, 200)
self.assertIn("token", login_response.json) self.assertIn("token", login_response.json)
response = self.client.post("/users/1/profile",
data=json.dumps({}),
content_type="application/json",
headers={"Authorization": "Bearer " + login_response.json["token"]})
self.assertEqual(response.status_code, 403)
def test_update_profile(self):
token = self.login("peter", "geheim")["token"]
data = { data = {
"pronouns": "Monsieur", "pronouns": "Herr Dr. Dr.",
"volunteerwork": "ja", "volunteerwork": "ja",
"freetext": "Hallo", "freetext": "Hallo",
"address": {
"name": "Peeeda",
"street": "Bachstraße",
"house_number": "42x",
"additional": "oben",
"postcode": "23232",
"city": "Travemünde",
"country": "Deutschland"
},
"contacts": [{
"id": 1,
"contacttype": {
"id": 1,
"name": "Matrix"
},
"content": "@peeda:wtf-eg.de"
}, {
"contacttype": {
"name": "Rohrpost"
},
"content": "Ausgang 2"
}],
"skills": [{
"id": 1,
"skill": {
"id": 3,
"name": "Python"
},
"level": 4
}, {
"skill": {
"name": "Tschunkproduktion"
},
"level": 5
}],
"languages": [{
"id": 1,
"language": {
"id": "de",
"name": "Deutsch"
},
"level": 4
}, {
"language": {
"id": "es",
"name": "Spanisch"
},
"level": 2
}]
} }
response = self.client.post("/users/1/profile", response = self.client.post("/users/1/profile",
data=json.dumps(data), data=json.dumps(data),
content_type="application/json", content_type="application/json",
headers={"Authorization": "Bearer " + token}) headers={
"Authorization":
"Bearer " +
login_response.json["token"]
})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
with app.app_context(): with app.app_context():
user = User.query.filter(User.id == 1).first() user = User.query.filter(User.id == 1).first()
profile = user.profile profile = user.profile
self.assertEqual("Monsieur", profile.pronouns) self.assertEqual("Herr Dr. Dr.", profile.pronouns)
self.assertEqual("ja", profile.volunteerwork) self.assertEqual("ja", profile.volunteerwork)
self.assertEqual("Hallo", profile.freetext) self.assertEqual("Hallo", profile.freetext)
address = profile.address
self.assertEqual(address.name, "Peeeda")
self.assertEqual(address.street, "Bachstraße")
self.assertEqual(address.house_number, "42x")
self.assertEqual(address.additional, "oben")
self.assertEqual(address.postcode, "23232")
self.assertEqual(address.city, "Travemünde")
self.assertEqual(address.country, "Deutschland")
contacts = profile.contacts
self.assertEqual(len(contacts), 2)
first_contact = contacts[0]
self.assertEqual(first_contact.contacttype.name, "Matrix")
self.assertEqual(first_contact.content, "@peeda:wtf-eg.de")
second_contact = contacts[1]
self.assertEqual(second_contact.contacttype.name, "Rohrpost")
self.assertEqual(second_contact.content, "Ausgang 2")
skills = profile.skills
self.assertEqual(len(skills), 2)
first_skill = skills[0]
self.assertEqual(first_skill.skill.id, 3)
self.assertEqual(first_skill.skill.name, "Python")
self.assertEqual(first_skill.level, 4)
second_skill = skills[1]
self.assertEqual(second_skill.skill.id, 13)
self.assertEqual(second_skill.skill.name, "Tschunkproduktion")
self.assertEqual(second_skill.level, 5)
languages = profile.languages
self.assertEqual(len(languages), 2)
first_language = languages[0]
self.assertEqual(first_language.language_id, "de")
self.assertEqual(first_language.level, 4)
second_language = languages[1]
self.assertEqual(second_language.language_id, "es")
self.assertEqual(second_language.level, 2)
def test_get_profile(self): def test_get_profile(self):
user = User(auth_id="peter")
db.session.add(user)
profile = Profile(user=user)
profile.nickname = "Popeter"
db.session.add(profile)
db.session.commit()
login_data = {"username": "peter", "password": "geheim"} login_data = {"username": "peter", "password": "geheim"}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json") login_response = self.client.post("/users/login",
data=json.dumps(login_data),
content_type="application/json")
self.assertEqual(login_response.status_code, 200) self.assertEqual(login_response.status_code, 200)
self.assertIn("token", login_response.json) self.assertIn("token", login_response.json)
response = self.client.get("/users/1/profile", response = self.client.get("/users/1/profile",
headers={"Authorization": "Bearer " + login_response.json["token"]}) headers={
"Authorization":
"Bearer " + login_response.json["token"]
})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertDictEqual( self.assertEqual(
response.json, { response.json, {
"profile": { "profile": {
"user_id": 1, "freetext": "",
"nickname": "peternichtlustig", "nickname": "Popeter",
"pronouns": "Herr Dr. Dr.", "pronouns": "",
"freetext": "Ich mag Kaffee", "volunteerwork": ""
"volunteerwork": "Gartenverein",
"address": {
"additional": "Hinterhaus",
"city": "Bielefeld",
"country": "Deutschland",
"house_number": "23i",
"id": 1,
"name": "Peter Nichtlustig",
"postcode": "13337",
"profile_id": 1,
"street": "Waldweg"
},
"contacts": [{
"id": 1,
"profile_id": 1,
"contacttype": {
"id": 1,
"name": "Matrix"
},
"content": "@peter:wtf-eg.de"
}, {
"id": 2,
"profile_id": 1,
"contacttype": {
"id": 2,
"name": "E-Mail"
},
"content": "peter@wtf-eg.de"
}],
"skills": [{
"profile_id": 1,
"skill": {
"id": 1,
"name": "PHP",
"icon_url": "/skills/1/icon"
},
"level": 5
}, {
"profile_id": 1,
"skill": {
"id": 3,
"name": "Python",
"icon_url": "/skills/3/icon"
},
"level": 3
}],
"languages": [{
"profile_id": 1,
"language": {
"id": "de",
"name": "Deutsch",
"icon_url": "/languages/de/icon"
},
"level": 5
}, {
"profile_id": 1,
"language": {
"id": "fr",
"name": "Französisch",
"icon_url": "/languages/fr/icon"
},
"level": 3
}]
} }
}) })

View File

@ -1,38 +1,34 @@
from alembic import command
import unittest import unittest
from ki.test.ApiTest import ApiTest from app import app, migrate
from ki.commands import seed
class TestSkillsEndpoint(ApiTest): class TestSkillsEndpoint(unittest.TestCase):
def test_skills_options(self): def setUp(self):
response = self.client.options("/skills") app.debug = True
self.assertEqual(response.status_code, 200) app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
self.assertIn("Access-Control-Allow-Origin", response.headers) self.client = app.test_client()
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
with app.app_context():
config = migrate.get_config()
command.upgrade(config, "head")
seed()
def test_get_skills1(self): def test_get_skills1(self):
token = self.login("peter", "geheim")["token"] response = self.client.get("/skills?search=p")
response = self.client.get("/skills?search=p", headers={"Authorization": "Bearer " + token})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual( self.assertEqual(
{ {
"skills": [{ "skills": [
"id": 1, {"id": 1, "name": "PHP"},
"name": "PHP", {"id": 3, "name": "Python"}
"icon_url": "/skills/1/icon" ]
}, { },
"id": 10, response.json
"name": "PostgreSQL", )
"icon_url": "/skills/10/icon"
}, {
"id": 3,
"name": "Python",
"icon_url": "/skills/3/icon"
}]
}, response.json)
self.assertIn("Access-Control-Allow-Origin", response.headers)
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
if __name__ == "main": if __name__ == "main":