forked from boos/BoosAPI
refactor: remove deprecated apiKey from site management and update related functionality
- Removed apiKey from SiteRow type and related forms in Sites component. - Updated site saving logic to handle site creation without apiKey. - Adjusted tests to ensure apiKey is not included in site editor state. - Refactored Tokens component to support embedded mode and redirect logic. - Added migration tests for site apiKey to account migration service. - Implemented siteApiKeyMigrationService to handle migration of apiKey to account tokens. - Enhanced tests for segmented connections in Accounts and redirect logic for Tokens.
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
type DbModule = typeof import('../db/index.js');
|
||||
type MigrationModule = typeof import('./siteApiKeyMigrationService.js');
|
||||
|
||||
describe('siteApiKeyMigrationService', () => {
|
||||
let db: DbModule['db'];
|
||||
let schema: DbModule['schema'];
|
||||
let migrateSiteApiKeysToAccounts: MigrationModule['migrateSiteApiKeysToAccounts'];
|
||||
let dataDir = '';
|
||||
|
||||
beforeAll(async () => {
|
||||
dataDir = mkdtempSync(join(tmpdir(), 'metapi-site-api-key-migration-'));
|
||||
process.env.DATA_DIR = dataDir;
|
||||
|
||||
await import('../db/migrate.js');
|
||||
const dbModule = await import('../db/index.js');
|
||||
const migrationModule = await import('./siteApiKeyMigrationService.js');
|
||||
db = dbModule.db;
|
||||
schema = dbModule.schema;
|
||||
migrateSiteApiKeysToAccounts = migrationModule.migrateSiteApiKeysToAccounts;
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
await db.delete(schema.accountTokens).run();
|
||||
await db.delete(schema.accounts).run();
|
||||
await db.delete(schema.sites).run();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
delete process.env.DATA_DIR;
|
||||
});
|
||||
|
||||
it('migrates site apiKey into an apikey connection, clears the site field, and removes a mirrored token row', async () => {
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'legacy-site',
|
||||
url: 'https://legacy.example.com',
|
||||
platform: 'new-api',
|
||||
apiKey: 'sk-legacy-site-token',
|
||||
}).returning().get();
|
||||
|
||||
const existing = await db.insert(schema.accounts).values({
|
||||
siteId: site.id,
|
||||
username: null,
|
||||
accessToken: '',
|
||||
apiToken: 'sk-legacy-site-token',
|
||||
checkinEnabled: false,
|
||||
extraConfig: JSON.stringify({ credentialMode: 'apikey' }),
|
||||
}).returning().get();
|
||||
|
||||
await db.insert(schema.accountTokens).values({
|
||||
accountId: existing.id,
|
||||
name: 'default',
|
||||
token: 'sk-legacy-site-token',
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
}).run();
|
||||
|
||||
const summary = await migrateSiteApiKeysToAccounts();
|
||||
|
||||
expect(summary).toMatchObject({
|
||||
migrated: 0,
|
||||
deduped: 1,
|
||||
clearedSites: 1,
|
||||
removedMirrorTokens: 1,
|
||||
warned: 0,
|
||||
});
|
||||
|
||||
const migratedSite = await db.select().from(schema.sites).where(eq(schema.sites.id, site.id)).get();
|
||||
expect(migratedSite?.apiKey).toBeNull();
|
||||
|
||||
const accounts = await db.select().from(schema.accounts).where(eq(schema.accounts.siteId, site.id)).all();
|
||||
expect(accounts).toHaveLength(1);
|
||||
expect(accounts[0]?.apiToken).toBe('sk-legacy-site-token');
|
||||
expect(accounts[0]?.accessToken).toBe('');
|
||||
expect(accounts[0]?.checkinEnabled).toBe(false);
|
||||
expect(JSON.parse(accounts[0]?.extraConfig || '{}')).toMatchObject({ credentialMode: 'apikey' });
|
||||
|
||||
const tokens = await db.select().from(schema.accountTokens).where(eq(schema.accountTokens.accountId, existing.id)).all();
|
||||
expect(tokens).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('creates a new apikey connection from site apiKey when no matching connection exists', async () => {
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'new-site',
|
||||
url: 'https://new-site.example.com',
|
||||
platform: 'new-api',
|
||||
apiKey: 'sk-new-site-token',
|
||||
}).returning().get();
|
||||
|
||||
const summary = await migrateSiteApiKeysToAccounts();
|
||||
|
||||
expect(summary).toMatchObject({
|
||||
migrated: 1,
|
||||
deduped: 0,
|
||||
clearedSites: 1,
|
||||
removedMirrorTokens: 0,
|
||||
warned: 0,
|
||||
});
|
||||
|
||||
const accounts = await db.select().from(schema.accounts).where(eq(schema.accounts.siteId, site.id)).all();
|
||||
expect(accounts).toHaveLength(1);
|
||||
expect(accounts[0]?.apiToken).toBe('sk-new-site-token');
|
||||
expect(accounts[0]?.accessToken).toBe('');
|
||||
expect(accounts[0]?.status).toBe('active');
|
||||
expect(JSON.parse(accounts[0]?.extraConfig || '{}')).toMatchObject({ credentialMode: 'apikey' });
|
||||
});
|
||||
|
||||
it('warns and keeps token rows intact when an apikey connection has multiple child tokens', async () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'warn-site',
|
||||
url: 'https://warn.example.com',
|
||||
platform: 'new-api',
|
||||
apiKey: 'sk-warn-site-token',
|
||||
}).returning().get();
|
||||
|
||||
const account = await db.insert(schema.accounts).values({
|
||||
siteId: site.id,
|
||||
username: null,
|
||||
accessToken: '',
|
||||
apiToken: 'sk-warn-site-token',
|
||||
checkinEnabled: false,
|
||||
extraConfig: JSON.stringify({ credentialMode: 'apikey' }),
|
||||
}).returning().get();
|
||||
|
||||
await db.insert(schema.accountTokens).values([
|
||||
{
|
||||
accountId: account.id,
|
||||
name: 'default',
|
||||
token: 'sk-warn-site-token',
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
accountId: account.id,
|
||||
name: 'extra',
|
||||
token: 'sk-extra-token',
|
||||
enabled: true,
|
||||
isDefault: false,
|
||||
},
|
||||
]).run();
|
||||
|
||||
const summary = await migrateSiteApiKeysToAccounts();
|
||||
|
||||
expect(summary.warned).toBe(1);
|
||||
expect(warnSpy).toHaveBeenCalled();
|
||||
|
||||
const tokens = await db.select().from(schema.accountTokens).where(eq(schema.accountTokens.accountId, account.id)).all();
|
||||
expect(tokens).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user