requirements.txt changed, auth.py file added for user management, announcement_service_spec.yaml changed since api definition did not load, main.py changes to access new mongoDB collection, patchnotes_controller.py expendet so save user last login
This commit is contained in:
parent
bdf0466d79
commit
7f8d7cbc86
|
|
@ -16,8 +16,6 @@ paths:
|
||||||
operationId: listPatchnotes
|
operationId: listPatchnotes
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/SinceParam'
|
- $ref: '#/components/parameters/SinceParam'
|
||||||
- $ref: '#/components/parameters/LimitParam'
|
|
||||||
- $ref: '#/components/parameters/OffsetParam'
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: list of patch notes
|
description: list of patch notes
|
||||||
|
|
@ -55,6 +53,23 @@ paths:
|
||||||
"400":
|
"400":
|
||||||
description: bad payload
|
description: bad payload
|
||||||
|
|
||||||
|
/patchnotes/all:
|
||||||
|
get:
|
||||||
|
tags: [Patchnotes]
|
||||||
|
summary: Returns **all** patch notes (admin area)
|
||||||
|
operationId: listAllPatchnotes
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: list of all patch notes
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/patch_notes'
|
||||||
|
"404":
|
||||||
|
description: no Patch notes found
|
||||||
|
|
||||||
/patchnotes/{patchID}:
|
/patchnotes/{patchID}:
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
|
|
@ -119,25 +134,6 @@ components:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
LimitParam:
|
|
||||||
name: limit
|
|
||||||
in: query
|
|
||||||
description: Maximum number of items to return
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
default: 20
|
|
||||||
minimum: 1
|
|
||||||
maximum: 100
|
|
||||||
OffsetParam:
|
|
||||||
name: offset
|
|
||||||
in: query
|
|
||||||
description: Pagination offset
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
default: 0
|
|
||||||
minimum: 0
|
|
||||||
|
|
||||||
schemas:
|
schemas:
|
||||||
patch_notes:
|
patch_notes:
|
||||||
|
|
@ -161,10 +157,10 @@ components:
|
||||||
title: Patch note 1
|
title: Patch note 1
|
||||||
changes: Fixed export bug
|
changes: Fixed export bug
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
patch_date: 2025-01-02
|
patch_date: '2025-01-02' # ← als String in Quotes!
|
||||||
|
|
||||||
patch_notes_input:
|
patch_notes_input:
|
||||||
description: Schema for POST/PUT (server generates patchID & patch_date if omitted)
|
description: Schema for POST/PUT (server generates patchID if omitted)
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
title:
|
title:
|
||||||
|
|
@ -177,6 +173,11 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
required: [title, changes, version, patch_date]
|
required: [title, changes, version, patch_date]
|
||||||
|
example:
|
||||||
|
title: Patch note 1
|
||||||
|
changes: Fixed export bug
|
||||||
|
version: 1.0.0
|
||||||
|
patch_date: '2025-01-02' # ← ebenfalls in Quotes
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
Error400:
|
Error400:
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,18 @@ from connexion.middleware import MiddlewarePosition
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
from openapi_server import encoder
|
from openapi_server import encoder
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient, ASCENDING
|
||||||
from starlette.middleware.cors import CORSMiddleware
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
import re
|
import re
|
||||||
|
|
||||||
client = MongoClient(host='mongodb://admin:admin@announcement-service-database-svc:27017/?authSource=admin')
|
client = MongoClient(host='mongodb://admin:admin@announcement-service-database-svc:27017/?authSource=admin')
|
||||||
db = client["MainDB"]
|
db = client["MainDB"]
|
||||||
collection = db['patch_notes']
|
collection = db['patch_notes']
|
||||||
|
last_login_collection = db["user_last_login"]
|
||||||
|
|
||||||
|
last_login_collection.create_index(
|
||||||
|
[("user_id", ASCENDING)], unique=True, name="user_id_unique"
|
||||||
|
)
|
||||||
|
|
||||||
app = connexion.FlaskApp(__name__, specification_dir='./openapi/')
|
app = connexion.FlaskApp(__name__, specification_dir='./openapi/')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,9 @@ from connexion import ProblemException
|
||||||
from flask import Response
|
from flask import Response
|
||||||
from __main__ import collection
|
from __main__ import collection
|
||||||
|
|
||||||
from openapi_server.__main__ import collection
|
from openapi_server.__main__ import collection, last_login_collection
|
||||||
from openapi_server.models.patch_notes import PatchNotes
|
from openapi_server.models.patch_notes import PatchNotes
|
||||||
|
from openapi_server.utils.auth import current_user_id
|
||||||
|
|
||||||
|
|
||||||
def create_patchnote(body): # noqa: E501
|
def create_patchnote(body): # noqa: E501
|
||||||
|
|
@ -119,36 +120,46 @@ def get_patchnote(patch_id): # noqa: E501
|
||||||
|
|
||||||
|
|
||||||
def list_patchnotes(since: str | None = None):
|
def list_patchnotes(since: str | None = None):
|
||||||
# 1) validate query param only if present
|
"""GET /patchnotes – liefert neue Patchnotes und setzt last_login."""
|
||||||
|
|
||||||
|
user_id = current_user_id() # 1) aktueller Benutzer
|
||||||
|
|
||||||
|
# 2) Wenn ?since fehlt, gespeichertes last_login holen
|
||||||
|
if since is None:
|
||||||
|
rec = last_login_collection.find_one({"user_id": user_id})
|
||||||
|
since = rec["last_login"] if rec else None
|
||||||
|
|
||||||
|
# 3) Datum validieren (nur wenn vorhanden)
|
||||||
if since:
|
if since:
|
||||||
try:
|
try:
|
||||||
datetime.strptime(since, "%Y-%m-%d")
|
datetime.strptime(since, "%Y-%m-%d")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ProblemException(
|
raise ProblemException(
|
||||||
title="Bad Request",
|
title="Bad Request",
|
||||||
detail="The date is invalid. Needs to be in this format YYYY-MM-DD",
|
detail="since must be YYYY-MM-DD",
|
||||||
status=400,
|
status=400,
|
||||||
)
|
)
|
||||||
query = {"patch_date": {"$gte": since}}
|
mongo_filter = {"patch_date": {"$gte": since}}
|
||||||
else:
|
else:
|
||||||
query = {} # no filter -> all notes
|
mongo_filter = {} # erster Login → alle Notes
|
||||||
|
|
||||||
# 2) fetch from Mongo
|
# 4) Patchnotes abholen
|
||||||
docs = list(collection.find(query).sort("patch_date", 1))
|
docs = list(collection.find(mongo_filter).sort("patch_date", 1))
|
||||||
|
|
||||||
# 3) ensure every patch_date is *string* before serialising
|
|
||||||
for d in docs:
|
for d in docs:
|
||||||
if isinstance(d.get("patch_date"), (datetime, date)):
|
d.pop("_id", None)
|
||||||
d["patch_date"] = d["patch_date"].strftime("%Y-%m-%d")
|
if isinstance(d["patch_date"], (datetime, date)):
|
||||||
d.pop("_id", None) # optional: hide Mongo’s internal id
|
d["patch_date"] = d["patch_date"].isoformat()
|
||||||
|
|
||||||
# 4) serialise – default=str handles any remaining non-JSON types
|
# 5) Heutiges Datum als neuen last_login speichern
|
||||||
return Response(
|
today = date.today().isoformat()
|
||||||
json_util.dumps(docs, default=str),
|
last_login_collection.update_one(
|
||||||
mimetype="application/json",
|
{"user_id": user_id},
|
||||||
status=200,
|
{"$set": {"last_login": today}},
|
||||||
|
upsert=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return Response(json_util.dumps(docs), mimetype="application/json", status=200)
|
||||||
|
|
||||||
|
|
||||||
def update_patchnote(patch_id, body): # noqa: E501
|
def update_patchnote(patch_id, body): # noqa: E501
|
||||||
"""Updates one patch note (full replace)
|
"""Updates one patch note (full replace)
|
||||||
|
|
@ -206,3 +217,5 @@ def update_patchnote(patch_id, body): # noqa: E501
|
||||||
mimetype="application/json",
|
mimetype="application/json",
|
||||||
status=200,
|
status=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import jwt
|
||||||
|
from connexion import ProblemException
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
def current_user_id() -> str:
|
||||||
|
"""Liest die User-UUID (sub) aus dem Bearer-JWT im Authorization-Header."""
|
||||||
|
auth = request.headers.get("Authorization", "")
|
||||||
|
if not auth.startswith("Bearer "):
|
||||||
|
raise ProblemException(status=401, detail="Missing Bearer token")
|
||||||
|
|
||||||
|
token = auth.split()[1]
|
||||||
|
|
||||||
|
# ↓ Für Demo ohne Signaturprüfung – produktiv natürlich verifizieren!
|
||||||
|
payload = jwt.decode(token, options={"verify_signature": False})
|
||||||
|
return payload["sub"]
|
||||||
|
|
@ -15,3 +15,6 @@ flask_cors >= 5.0.1
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
Flask-Testing==0.8.1
|
Flask-Testing==0.8.1
|
||||||
starlette==0.46.1
|
starlette==0.46.1
|
||||||
|
|
||||||
|
typing_extensions==4.12.2
|
||||||
|
PyJWT==2.10.1
|
||||||
Loading…
Reference in New Issue