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