feat: add migration script for uapi apisetid -> upappid
Script copies shared uapi records so each upapp gets its own copy, then updates the link from apisetid to upappid.
This commit is contained in:
parent
3d11ff3dc1
commit
f16aa302eb
130
scripts/migrate_uapi_upappid.py
Normal file
130
scripts/migrate_uapi_upappid.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
"""
|
||||||
|
Migration: uapi table apisetid -> upappid
|
||||||
|
|
||||||
|
Removes uapiset intermediate layer. Each upapp now owns its own uapi records.
|
||||||
|
For shared apisetid: copies uapi records so each upapp has its own copy.
|
||||||
|
|
||||||
|
Usage (in Sage virtual env):
|
||||||
|
./py3/bin/python3 ~/repos/uapi/scripts/migrate_uapi_upappid.py --output /tmp/migrate_uapi.sql
|
||||||
|
|
||||||
|
Review the output SQL, then execute on your database.
|
||||||
|
"""
|
||||||
|
from appPublic.getConfig import getConfig
|
||||||
|
import asyncio, json, sys, argparse
|
||||||
|
from sqlor.dbpools import DBPools
|
||||||
|
from appPublic.uniqueID import getID
|
||||||
|
|
||||||
|
config = getConfig('.')
|
||||||
|
db = DBPools(config.databases)
|
||||||
|
dbname = list(config.databases.keys())[0]
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_migration_sql():
|
||||||
|
"""Generate SQL to migrate uapi.apisetid -> uapi.upappid."""
|
||||||
|
lines = [
|
||||||
|
"-- Migration: uapi table apisetid -> upappid",
|
||||||
|
"-- Removes uapiset intermediate layer.",
|
||||||
|
"-- Each upapp owns its own uapi records after migration.",
|
||||||
|
"",
|
||||||
|
"-- Step 1: Add upappid column to uapi",
|
||||||
|
"ALTER TABLE uapi ADD COLUMN upappid VARCHAR(21) DEFAULT NULL COMMENT '上位系统ID' AFTER id;",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|
||||||
|
# Load data
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
# Get all upapps
|
||||||
|
upapps = await sor.sqlExe('select id, name, apisetid from upapp', {})
|
||||||
|
# Get all uapis
|
||||||
|
uapis = await sor.sqlExe('select id, apisetid, name, httpmethod, path, headers, ioid, auth_apiname, response, params, data, chunk_match from uapi', {})
|
||||||
|
|
||||||
|
# Build mapping: apisetid -> [upapp1, upapp2, ...]
|
||||||
|
apiset_to_upapps = {}
|
||||||
|
for u in upapps:
|
||||||
|
aid = u.get('apisetid')
|
||||||
|
if aid:
|
||||||
|
apiset_to_upapps.setdefault(aid, []).append(u)
|
||||||
|
|
||||||
|
# Build mapping: apisetid -> [uapi_records]
|
||||||
|
apiset_to_uapis = {}
|
||||||
|
for a in uapis:
|
||||||
|
aid = a.get('apisetid')
|
||||||
|
if aid:
|
||||||
|
apiset_to_uapis.setdefault(aid, []).append(a)
|
||||||
|
|
||||||
|
inserts = []
|
||||||
|
for apisetid, upapp_list in apiset_to_upapps.items():
|
||||||
|
uapi_records = apiset_to_uapis.get(apisetid, [])
|
||||||
|
|
||||||
|
if len(upapp_list) == 1:
|
||||||
|
# Single upapp owns this apisetid -> just update
|
||||||
|
upapp = upapp_list[0]
|
||||||
|
for uapi in uapi_records:
|
||||||
|
inserts.append(
|
||||||
|
f"UPDATE uapi SET upappid = '{upapp['id']}' WHERE id = '{uapi['id']}';"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Multiple upapps share this apisetid -> pick first as owner, copy for rest
|
||||||
|
owner = upapp_list[0]
|
||||||
|
|
||||||
|
# Update existing records to point to owner
|
||||||
|
for uapi in uapi_records:
|
||||||
|
inserts.append(
|
||||||
|
f"UPDATE uapi SET upappid = '{owner['id']}' WHERE id = '{uapi['id']}';"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Copy for other upapps
|
||||||
|
for other_upapp in upapp_list[1:]:
|
||||||
|
for uapi in uapi_records:
|
||||||
|
new_id = getID()
|
||||||
|
old_id = uapi['id']
|
||||||
|
fields = ['id', 'upappid', 'name', 'httpmethod', 'path', 'headers', 'ioid', 'response', 'params', 'data', 'chunk_match']
|
||||||
|
vals = [f"'{new_id}'", f"'{other_upapp['id']}'"]
|
||||||
|
for f in fields[2:]:
|
||||||
|
v = uapi.get(f)
|
||||||
|
if v is None:
|
||||||
|
vals.append('NULL')
|
||||||
|
elif isinstance(v, str):
|
||||||
|
escaped = v.replace("'", "\\'")
|
||||||
|
vals.append(f"'{escaped}'")
|
||||||
|
else:
|
||||||
|
vals.append(str(v))
|
||||||
|
col_str = ', '.join(fields)
|
||||||
|
val_str = ', '.join(vals)
|
||||||
|
inserts.append(f"INSERT INTO uapi ({col_str}) VALUES ({val_str});")
|
||||||
|
|
||||||
|
if inserts:
|
||||||
|
lines.append("-- Step 2: Migrate data (updates + copies for shared apisetid)")
|
||||||
|
lines.extend(inserts)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.extend([
|
||||||
|
"-- Step 3: Drop apisetid column (verify upappid has no NULLs first)",
|
||||||
|
"-- UPDATE uapi SET upappid = (SELECT ownerid FROM organization LIMIT 1) WHERE upappid IS NULL;",
|
||||||
|
"-- ALTER TABLE uapi DROP COLUMN apisetid;",
|
||||||
|
"",
|
||||||
|
"-- Step 4: Add index",
|
||||||
|
"CREATE INDEX idx_uapi_upappid ON uapi (upappid);",
|
||||||
|
""
|
||||||
|
])
|
||||||
|
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--output', '-o', default='-')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
sql = await generate_migration_sql()
|
||||||
|
|
||||||
|
if args.output == '-':
|
||||||
|
print(sql)
|
||||||
|
else:
|
||||||
|
with open(args.output, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(sql)
|
||||||
|
print(f"Generated migration SQL -> {args.output}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
Loading…
x
Reference in New Issue
Block a user