Add the API import surface for legacy migrations (contract 0.8.0)
Goal: an LLM or script can migrate an entire legacy database using only the HTTP API - original history preserved, safely re-runnable. - X-Import-Mode header (admin only): create/update endpoints across 15 timestamped entity types accept original createddate/modifieddate; helper exposed via shopdb.api (contract 0.7.0 -> 0.8.0). - Exact-match natural-key lookup filters on 13 list endpoints for the lookup-then-upsert recipe. - Selfhosted USB checkout/checkin accept backdated event times in import mode. - docs/IMPORT-API.md: operator manual grounded in the real legacy schema - order of operations, full table-by-table mapping including the machines fan-out, idempotent Python importer with dry-run, parity checks, and decided dispositions for unmigrated tables (DNC config stays live-fed via the collector; supportteams/appowners map to the upcoming supportteams model). 635 tests pass; naming green; frontend untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ from shopdb.api import (
|
||||
|
||||
from ..models import KnowledgeBase
|
||||
|
||||
from shopdb.api import require_permission, require_role
|
||||
from shopdb.api import require_permission, require_role, apply_import_timestamps
|
||||
|
||||
knowledgebase_bp = Blueprint('knowledgebase', __name__)
|
||||
|
||||
@@ -42,6 +42,13 @@ def list_articles():
|
||||
if appid := request.args.get('appid'):
|
||||
query = query.filter(KnowledgeBase.appid == int(appid))
|
||||
|
||||
# Exact-match natural-key lookups for idempotent import. linkurl is the
|
||||
# stable natural key; shortdescription (the title) is offered as a fallback.
|
||||
if exactlinkurl := request.args.get('linkurl'):
|
||||
query = query.filter(KnowledgeBase.linkurl == exactlinkurl)
|
||||
if exacttitle := request.args.get('shortdescription'):
|
||||
query = query.filter(KnowledgeBase.shortdescription == exacttitle)
|
||||
|
||||
# Sort options
|
||||
sort = request.args.get('sort', 'clicks')
|
||||
order = request.args.get('order', 'desc')
|
||||
@@ -165,6 +172,7 @@ def create_article():
|
||||
)
|
||||
|
||||
db.session.add(article)
|
||||
apply_import_timestamps(article, data)
|
||||
db.session.commit()
|
||||
|
||||
return success_response(article.to_dict(), message='Article created', http_code=201)
|
||||
@@ -195,6 +203,7 @@ def update_article(link_id: int):
|
||||
if key in data:
|
||||
setattr(article, key, data[key])
|
||||
|
||||
apply_import_timestamps(article, data)
|
||||
db.session.commit()
|
||||
return success_response(article.to_dict(), message='Article updated')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user