Compare commits

..

49 Commits

Author SHA1 Message Date
c4f5979d95 added model and migration for resume
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-30 19:13:43 +02:00
92f5393a4c added nullable false to prevent unexpected migration of this table 2024-08-30 19:11:03 +02:00
383ef8b512 initial (empty) blueprint and model 2024-08-30 19:11:03 +02:00
033dee7836 Merge pull request 'Update dependency python-dotenv to v0.21.1' (!91) from renovate/python-dotenv-0.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #91
2024-08-28 16:27:46 +02:00
ca81e8bf70 Merge branch 'main' into renovate/python-dotenv-0.x
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 16:02:01 +02:00
d507a20a93 Merge pull request 'Update dependency flake8 to v6' (!107) from renovate/flake8-6.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #107
2024-08-28 15:52:02 +02:00
3f2c23c386 Merge branch 'main' into renovate/flake8-6.x
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 15:46:31 +02:00
b46ac5e379 Merge pull request 'Update dependency pymysql to v1.1.1' (!90) from renovate/pymysql-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #90
2024-08-28 15:43:17 +02:00
fa4429b6ef Update dependency flake8 to v6
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 13:37:17 +00:00
7a0f2434db Update dependency python-dotenv to v0.21.1
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 13:36:27 +00:00
8c3fe3fe7d Update dependency pymysql to v1.1.1
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 13:36:11 +00:00
56ade6de68 Merge pull request 'Update dependency flask to v2.3.3' (!87) from renovate/flask-2.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #87
Reviewed-by: Brain <brain@noreply.git.wtf-eg.de>
2024-08-28 15:19:36 +02:00
28cf714217 Update dependency flask to v2.3.3
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 12:35:38 +00:00
9ff56f6676 Merge pull request 'Update dependency yapf to v0.40.2' (!94) from renovate/yapf-0.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #94
2024-08-28 13:46:17 +02:00
2412df4960 Apply yapf and add ignorefile
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 13:43:33 +02:00
469ef511d6 Merge pull request 'Add .python-version file' (!105) from pyenv into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #105
2024-08-28 13:19:28 +02:00
47d2c94b79 Specify license for .python-version
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 12:57:31 +02:00
384dd82454 Update dependency yapf to v0.40.2
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-27 17:38:01 +00:00
b0dcfacd25 Add .python-version file
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-27 19:08:52 +02:00
86edb246bf Merge pull request 'revert f02efab07aeabe02a94efd62fbe498659297b36f' (!102) from revert-python-3.11 into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #102
2024-08-23 15:59:55 +02:00
9424e21edc revert f02efab07a
All checks were successful
continuous-integration/drone/pr Build is passing
revert Merge pull request 'Update python Docker tag to v3.11.9' (!97) from renovate/minor-3.11-python into main

Reviewed-on: #97
2024-08-23 15:50:30 +02:00
f02efab07a Merge pull request 'Update python Docker tag to v3.11.9' (!97) from renovate/minor-3.11-python into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #97
2024-08-23 15:33:32 +02:00
08f1104942 Merge branch 'main' into renovate/minor-3.11-python
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-23 15:30:29 +02:00
8dde142f38 Try and use Python 3.11
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-23 15:26:39 +02:00
d1b1636aa2 Merge pull request 'Update dependency waitress to v2.1.2' (!93) from renovate/waitress-2.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #93
2024-08-23 12:01:59 +02:00
3fcd1fa20e Update python Docker tag to v3.11.9
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-22 18:37:13 +00:00
8394400e96 Update dependency waitress to v2.1.2
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 18:36:55 +00:00
843050f923 Merge pull request 'Update plugins/docker Docker tag to v20.18.4' (!95) from renovate/plugins-docker-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #95
2024-08-22 19:53:59 +02:00
812913ffe2 Merge pull request 'Update dependency reuse to v0.14.0' (!92) from renovate/reuse-0.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #92
2024-08-22 19:52:24 +02:00
dee80c7e14 Update plugins/docker Docker tag to v20.18.4
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 15:35:49 +00:00
de60ec0d46 Update dependency reuse to v0.14.0
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 14:35:41 +00:00
6c6fcea81c Merge pull request 'Update python Docker tag to v3.8.19' (!85) from renovate/patch-python into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #85
2024-08-22 13:17:55 +02:00
166e0d40c6 Merge pull request 'Update dependency werkzeug to v2.3.8' (!84) from renovate/werkzeug-2.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #84
2024-08-22 12:39:36 +02:00
c9b5ab62ed Update python Docker tag to v3.8.19
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 10:35:35 +00:00
c0005100b4 Update dependency werkzeug to v2.3.8
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 10:35:34 +00:00
d614039cdd Merge pull request 'Pin python Docker tag to 0ef73cd' (!83) from renovate/python into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #83
2024-08-22 11:52:25 +02:00
316236a7e5 Merge pull request 'Pin dependencies' (!82) from renovate/pin-dependencies into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #82
2024-08-22 11:51:54 +02:00
c15faabd6d Pinning werkzeug to v2
All checks were successful
continuous-integration/drone/pr Build is passing
Thanks for the hint in #78
2024-08-22 11:46:27 +02:00
53eab74e60 Pin python Docker tag to 0ef73cd
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 09:35:43 +00:00
82d908193c Pin dependencies
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-22 09:35:42 +00:00
6620a6819a Merge pull request 'Configure Renovate' (!81) from renovate/configure into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #81
2024-08-22 11:25:28 +02:00
be6fec18ab Update Renovate config
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 22:12:27 +02:00
9ebae03550 Add renovate.json
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-21 19:34:54 +00:00
763a6efc9f Merge pull request 'Improve Docker usage' (!80) from docker-improvements into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #80
2024-08-21 21:22:40 +02:00
6f04d23e6c Add license information to .dockerignore file
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 21:17:33 +02:00
155ddc556c Add .dockerignore file
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-21 21:09:12 +02:00
a5a85e6032 Fix Dockerfile warnings 2024-08-21 21:08:52 +02:00
6a3458a596 Pin Docker images in CI 2024-08-21 20:53:59 +02:00
97be8f4667 Ditch builder and base Docker images
This should make dependency upgrades easier and bring more clarity to the CI pipelines
2024-08-21 20:49:47 +02:00
27 changed files with 706 additions and 529 deletions

13
.dockerignore Normal file
View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
*
!Pipfile
!Pipfile.lock
!data/
!ki/
!LICENSES/
!migrations/
!app.py
!run_prod.py

View File

@ -15,16 +15,18 @@ trigger:
steps:
- name: install-lint-test
image: git.wtf-eg.de/kompetenzinventar/builder:1.0.2
image: python:3.8.19-alpine@sha256:3bd7ea88cb637e09d6c7de24c5394657163a85c2be82bfebe0305cf07f8de1ea
env:
PYROOT: '/pyroot'
PYTHONUSERBASE: '/pyroot'
commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv install --dev
- pipenv run flake8
- pipenv run reuse lint
- pipenv run python -m unittest discover ki
image_pull_secrets:
- dockerconfig
---
kind: pipeline
type: docker
@ -41,7 +43,7 @@ depends_on:
steps:
- name: docker-publish
image: plugins/docker
image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
settings:
registry: git.wtf-eg.de
repo: git.wtf-eg.de/kompetenzinventar/backend
@ -68,7 +70,7 @@ depends_on:
steps:
- name: deploy-dev
image: appleboy/drone-ssh
image: appleboy/drone-ssh:1.7.5@sha256:995677e073454912f26d4c0fdd2f9df2e1f5a30d6603d3f2ece667311b6babb3
settings:
host:
- dev01.wtf-eg.net
@ -91,14 +93,19 @@ trigger:
steps:
- name: install-lint-test
image: git.wtf-eg.de/kompetenzinventar/builder:1.0.2
image: python:3.8.19-alpine@sha256:3bd7ea88cb637e09d6c7de24c5394657163a85c2be82bfebe0305cf07f8de1ea
env:
PYROOT: '/pyroot'
PYTHONUSERBASE: '/pyroot'
commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv install --dev
- pipenv run flake8
- pipenv run reuse lint
- pipenv run python -m unittest discover ki
- name: docker-publish
image: plugins/docker
image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
settings:
registry: git.wtf-eg.de
repo: git.wtf-eg.de/kompetenzinventar/backend

View File

@ -27,5 +27,5 @@ repos:
name: reuse
entry: reuse lint
language: system
exclude: .*
exclude: ^(venv).*$
always_run: true

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.8.19

View File

@ -10,3 +10,7 @@ License: MIT
Files: Pipfile.lock migrations/*
Copyright: WTF Kooperative eG <https://wtf-eg.de/>
License: AGPL-3.0-or-later
Files: renovate.json .python-version
Copyright: WTF Kooperative eG <https://wtf-eg.de/>
License: AGPL-3.0-or-later

View File

@ -2,5 +2,4 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
*
!.gitignore
migrations/*.py

View File

@ -2,7 +2,17 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
FROM git.wtf-eg.de/kompetenzinventar/builder:1.0.2 as builder
FROM python:3.8.19-alpine@sha256:3bd7ea88cb637e09d6c7de24c5394657163a85c2be82bfebe0305cf07f8de1ea AS builder
ENV PYROOT=/pyroot
ENV PYTHONUSERBASE=$PYROOT
RUN apk add --no-cache \
gcc \
g++ \
musl-dev \
python3-dev && \
pip3 install pipenv
COPY Pipfile* ./
@ -10,7 +20,10 @@ RUN PIP_USER=1 PIP_IGNORE_INSTALLED=1 pipenv install --system --deploy --ignore-
RUN pip3 uninstall --yes pipenv
FROM git.wtf-eg.de/kompetenzinventar/base:1.0.2 as ki-backend
FROM python:3.8.19-alpine@sha256:3bd7ea88cb637e09d6c7de24c5394657163a85c2be82bfebe0305cf07f8de1ea AS ki-backend
ENV PYROOT=/pyroot
ENV PYTHONUSERBASE=$PYROOT
# Install six explicitly. Otherwise Python complains about it missing.
RUN pip3 install six

34
Pipfile
View File

@ -8,26 +8,26 @@ verify_ssl = true
name = "pypi"
[packages]
flask = "~=3.0.0"
python-dotenv = "~=0.17.1"
flask-migrate = "~=4.0.5"
flask-sqlalchemy = "~=3.1.1"
sqlalchemy = "~=2.0.25"
waitress = "~=2.1.2"
pyyaml = "~=6.0.1"
flask-cors = "~=4.0.0"
ldap3 = "~=2.9.1"
pymysql = "~=1.1.0"
werkzeug = "~=3.0.1"
flask = "==2.3.3"
python-dotenv = "==0.21.1"
flask-migrate = "==3.0.1"
flask-sqlalchemy = "==2.5.1"
sqlalchemy = "==1.4.53"
waitress = "==2.1.2"
pyyaml = "==6.0.2"
flask-cors = "==3.0.10"
ldap3 = "==2.9.1"
pymysql = "==1.1.1"
werkzeug = "==2.3.8"
[dev-packages]
flake8 = "~=3.9.2"
yapf = "~=0.40.2"
pre-commit = "~=2.13.0"
reuse = "~=0.13.0"
flake8 = "==6.1.0"
yapf = "==0.40.2"
pre-commit = "==2.13.0"
reuse = "==0.14.0"
[requires]
python_version = "3.11"
python_version = "3.8"
[scripts]
clean = "rm storage/ki.sqlite"
clean = "rm data/ki.sqlite"

834
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

7
app.py
View File

@ -38,9 +38,12 @@ app.config["KI_LDAP_AUTH_PASSWORD"] = os.getenv("KI_LDAP_AUTH_PASSWORD")
app.config["KI_LDAP_BASE_DN"] = os.getenv("KI_LDAP_BASE_DN")
CORS(app)
db = SQLAlchemy(app, session_options={"future": True})
db = SQLAlchemy(app)
migrate = Migrate(app, db, compare_type=True)
app.logger.info("Hello from KI")
from ki import module # noqa
from ki import resume
app.register_blueprint(resume.bp_resume, url_prefix='/resume')

View File

@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
SQLALCHEMY_DATABASE_URI=sqlite:///../storage/ki.sqlite
SQLALCHEMY_DATABASE_URI=sqlite:///storage/ki.sqlite
CORS_ORIGINS=*

View File

@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
import csv
@ -18,7 +19,7 @@ def seed_contacttypes():
for contacttype in csv_reader:
id = int(contacttype["id"])
db_contacttype = db.session.get(ContactType, id)
db_contacttype = ContactType.query.get(id)
if db_contacttype is None:
db.session.add(ContactType(id=int(contacttype["id"]), name=contacttype["name"]))
@ -98,7 +99,7 @@ def seed(dev: bool):
for skill in skills_csv_reader:
id = int(skill["id"])
db_skill = db.session.get(Skill, id)
db_skill = Skill.query.get(id)
if db_skill is None:
db.session.add(Skill(id=int(skill["id"]), name=skill["name"]))
@ -112,7 +113,7 @@ def seed(dev: bool):
for iso in iso_csv_reader:
id = iso["639-1"]
db_language = db.session.get(Language, id)
db_language = Language.query.get(id)
if db_language is None:
db.session.add(Language(id=iso["639-1"], name=iso["Sprache"]))
@ -146,13 +147,12 @@ def seed(dev: bool):
freetext="1001010010111!!!",
skills=[(1, 5)],
address=("Friedrich Witzig", "", "", "", "", "", ""))
# all_skills = [(skill.id, 3) for skill in Skill.query.all()] # query causes problems
# seed_user("jutta", languages=[("fr", 5)], skills=all_skills)
all_skills = [(skill.id, 3) for skill in Skill.query.all()]
seed_user("jutta", languages=[("fr", 5)], skills=all_skills)
seed_user("giesela", skills=[(9, 3), (10, 5)])
seed_user("bertha", visible=False, skills=[(11, 3), (10, 5)])
seed_user("monique", languages=[("fr", 4)])
print("seeding done")
with app.app_context():
db.session.commit() # also problematic
print("commit done")
db.session.commit()

View File

@ -33,7 +33,7 @@ def update_languages(profile, languages_data):
if "id" not in language_data["language"]:
continue
language = db.session.get(Language, language_data["language"]["id"])
language = Language.query.get(language_data["language"]["id"])
profile_language = ProfileLanguage.query.filter(ProfileLanguage.profile == profile,
ProfileLanguage.language == language).first()
@ -110,7 +110,7 @@ def update_contacts(profile, contacts_data):
if "id" in contact_data:
contact_id = int(contact_data["id"])
contact_ids_to_be_deleted.remove(contact_id)
contact = db.session.get(Contact, contact_id)
contact = Contact.query.get(contact_id)
else:
contact = Contact(profile=profile, contacttype=contacttype)
db.session.add(contact)
@ -122,7 +122,7 @@ def update_contacts(profile, contacts_data):
def update_profile(user_id: int):
user = db.session.get(User, user_id)
user = User.query.get(user_id)
if user is None:
return make_response({}, 404)

View File

@ -33,7 +33,7 @@ class Profile(db.Model):
volunteerwork = Column(String(4000), default="")
freetext = Column(String(4000), default="")
availability_status = Column(Boolean, default=False)
availability_status = Column(Boolean, default=False, nullable=False)
availability_text = Column(String(4000), default="")
availability_hours_per_week = Column(Integer, default=0)

32
ki/resume.py Normal file
View File

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
from flask import Blueprint
from ki.token_auth import token_auth
from ki.resume_models import Resume
bp_resume = Blueprint('resume', __name__,
template_folder='templates')
@bp_resume.route('/')
@token_auth
def show(page):
"""
return the list of resumes as object with data array inside
"""
pass
@bp_resume.route("/<resume_id>")
@token_auth
def get_resume(resume_id):
"""
lookup for resume with resume_id, check if its from this user
and provide its contents in the appropriate format
shall support 'format' parameter with values of 'html', 'pdf'
if no parameter is given, json is returned
"""
r = Resume()
return r.to_dict()

28
ki/resume_models.py Normal file
View File

@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
from sqlalchemy import Column, Integer, String, ForeignKey, JSON
from sqlalchemy.orm import relationship
from app import db
class Resume(db.Model):
__tablename__ = 'resume'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("user.id", ondelete='CASCADE'))
label = Column("label", String(50), nullable=True)
data = Column('data', JSON)
user = relationship("User", backref='user', passive_deletes=True)
def to_dict(self):
return {
"id": self.id,
'user_id': self.user_id,
"label": self.label,
"data": self.data
}

View File

@ -4,42 +4,18 @@
import os
from flask import g, make_response, request, send_file
from functools import wraps
from ki.auth import auth
from ki.handlers import find_profiles as find_profiles_handler
from ki.handlers import update_profile as update_profile_handler
from ki.models import ContactType, Language, Skill, Token, User
from app import app, db
from ki.models import ContactType, Language, Skill, User
from app import app
from ki.token_auth import token_auth
content_type_svg = "image/svg+xml"
content_type_png = "image/png"
def token_auth(func):
@wraps(func)
def _token_auth(*args, **kwargs):
auth_header = request.headers.get("Authorization")
if (auth_header is None):
return make_response({}, 401)
if not auth_header.startswith("Bearer"):
return make_response({}, 401)
token = Token.query.filter(Token.token == auth_header[7:]).first()
if token is None:
return make_response({}, 403)
g.user = token.user
return func(*args, **kwargs)
return _token_auth
def models_to_list(models):
models_list = []
@ -66,7 +42,7 @@ def handle_completion_request(model, key):
def handle_icon_request(model, id, path):
object = db.session.get(model, id)
object = model.query.get(id)
if object is None:
return make_response({}, 404)

View File

@ -1,5 +1,6 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
from alembic import command
import json
@ -8,14 +9,12 @@ import unittest
from app import app, db, migrate
from ki.actions import seed
from ki.models import Skill
from sqlalchemy import select
class ApiTest(unittest.TestCase):
maxDiff = None
def setUp(self):
print("Running setup")
app.debug = True
app.config["KI_AUTH"] = "file"
app.config["TESTING"] = True
@ -23,38 +22,19 @@ class ApiTest(unittest.TestCase):
self.client = app.test_client()
config = migrate.get_config()
with app.app_context():
config = migrate.get_config()
command.upgrade(config, "head")
seed(True)
# statement = select(Skill).order_by(Skill.id.desc())
# print(statement)
# skill_obj = db.session.scalars(statement).all()
# print(skill_obj)
# statement = select(Skill.id)
# print(statement)
# max_skill = db.session.Skill().order_by(Skill.id.desc()).first()
# max_skill = Skill.query.order_by(Skill.id.desc()).first() # TODO: problematic
with db.session.no_autoflush: # only works on first test run
max_skill = db.session.query(Skill).order_by(Skill.id.desc()).first() # TODO: also problematic,
# skills = db.session.execute(db.select(Skill)).scalars()
# print(max_skill)
# max_skill = db.session.execute(db.select(Skill)
# .order_by(Skill.id.desc())
# ).scalar_one()
print(max_skill)
print("max_skill done")
seed(True)
max_skill = Skill.query.order_by(Skill.id.desc()).first()
self.max_skill_id = max_skill.id
def tearDown(self):
print("Running teardown")
with app.app_context():
db.drop_all()
db.engine.dispose()
db.drop_all()
db.engine.dispose()
def login(self, username, password):
# with app.app_context():
login_data = {"username": username, "password": password}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json")

View File

@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
import unittest
@ -7,39 +8,37 @@ from ki.test.ApiTest import ApiTest
class TestContactTypesEndpoint(ApiTest):
def test_skills_options(self):
print("test_skills_options")
# with app.app_context():
response = self.client.options("/contacttypes")
self.assertEqual(response.status_code, 200)
self.assertIn("Access-Control-Allow-Origin", response.headers)
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
# def test_get_contacttypes_unauthorised(self):
# print("test_get_contacttypes_unauthorised")
# response = self.client.get("/contacttypes?search=m")
# self.assertEqual(response.status_code, 401)
def test_get_contacttypes_unauthorised(self):
response = self.client.get("/contacttypes?search=m")
self.assertEqual(response.status_code, 401)
# def test_get_contacttypes(self):
# token = self.login("peter", "geheim")["token"]
def test_get_contacttypes(self):
token = self.login("peter", "geheim")["token"]
# response = self.client.get("/contacttypes?search=m", headers={"Authorization": "Bearer " + token})
# self.assertEqual(response.status_code, 200)
# self.assertEqual(
# {
# "contacttypes": [{
# "id": 5,
# "name": "Mastodon"
# }, {
# "id": 4,
# "name": "Matrix"
# }, {
# "id": 2,
# "name": "Mobiltelefon"
# }]
# }, response.json)
# self.assertIn("Access-Control-Allow-Origin", response.headers)
# self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
response = self.client.get("/contacttypes?search=m", headers={"Authorization": "Bearer " + token})
self.assertEqual(response.status_code, 200)
self.assertEqual(
{
"contacttypes": [{
"id": 5,
"name": "Mastodon"
}, {
"id": 4,
"name": "Matrix"
}, {
"id": 2,
"name": "Mobiltelefon"
}]
}, response.json)
self.assertIn("Access-Control-Allow-Origin", response.headers)
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
if __name__ == "main":

View File

@ -8,6 +8,7 @@ from ki.test.ApiTest import ApiTest
class TestFindProfilesEndpoint(ApiTest):
def test_find_profiles_options(self):
response = self.client.options("/users/profiles")
self.assertEqual(response.status_code, 200)

View File

@ -8,6 +8,7 @@ from ki.test.ApiTest import ApiTest
class TestLanguagesEndpoint(ApiTest):
def test_skills_options(self):
response = self.client.options("/languages")
self.assertEqual(response.status_code, 200)

View File

@ -10,6 +10,7 @@ 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")

View File

@ -8,6 +8,7 @@ from ki.test.ApiTest import ApiTest
class TestSkillsEndpoint(ApiTest):
def test_skills_options(self):
response = self.client.options("/skills")
self.assertEqual(response.status_code, 200)

31
ki/token_auth.py Normal file
View File

@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
from flask import g, make_response, request
from functools import wraps
from ki.models import Token
def token_auth(func):
@wraps(func)
def _token_auth(*args, **kwargs):
auth_header = request.headers.get("Authorization")
if (auth_header is None):
return make_response({}, 401)
if not auth_header.startswith("Bearer"):
return make_response({}, 401)
token = Token.query.filter(Token.token == auth_header[7:]).first()
if token is None:
return make_response({}, 403)
g.user = token.user
return func(*args, **kwargs)
return _token_auth

View File

@ -19,7 +19,7 @@ logger = logging.getLogger('alembic.env')
# target_metadata = mymodel.Base.metadata
config.set_main_option(
'sqlalchemy.url',
str(current_app.extensions['migrate'].db.engine.url).replace(
str(current_app.extensions['migrate'].db.get_engine().url).replace(
'%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata
@ -68,7 +68,7 @@ def run_migrations_online():
directives[:] = []
logger.info('No changes in schema detected.')
connectable = current_app.extensions['migrate'].db.engine
connectable = current_app.extensions['migrate'].db.get_engine()
with connectable.connect() as connection:
context.configure(

View File

@ -0,0 +1,35 @@
"""add resume
Revision ID: 6be5073423b4
Revises: b5023977cbda
Create Date: 2024-08-30 18:18:14.555874
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6be5073423b4'
down_revision = 'b5023977cbda'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('resume',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('label', sa.String(length=50), nullable=True),
sa.Column('data', sa.JSON(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('resume')
# ### end Alembic commands ###

18
renovate.json Normal file
View File

@ -0,0 +1,18 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:best-practices",
":disableDependencyDashboard",
":maintainLockFilesMonthly",
":pinVersions",
":separateMultipleMajorReleases"
],
"packageRules": [
{
"matchDepNames": ["python"],
"groupName": "Python",
"separateMinorPatch": true,
"separateMultipleMinor": true
}
]
}