Script copies shared uapi records so each upapp gets its own copy, then updates the link from apisetid to upappid.
131 lines
4.6 KiB
Python
131 lines
4.6 KiB
Python
"""
|
|
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())
|