Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions graphql/server/src/middleware/graphile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ const buildPreset = (
context['jwt.claims.device_token'] = req.deviceToken;
}

// Export category exclusion — set 'platform_mirror' as default so
// platform-integrated mirror triggers are always excluded from
// db_migrate.sql_actions queries via the export_category_filter RLS
// policy. The X-Exclude-Categories header can override if needed.
context['export.exclude_categories'] = req.get('X-Exclude-Categories') || 'platform_mirror';

if (req.token?.user_id) {
const pgSettings: Record<string, string> = {
role: roleName,
Expand Down
8 changes: 5 additions & 3 deletions pgpm/export/src/export-graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
installMissingModules,
makeReplacer,
preparePackage,
normalizeOutdir
normalizeOutdir,
filterPlatformLeakage
} from './export-utils';

// =============================================================================
Expand Down Expand Up @@ -204,8 +205,9 @@ export const exportGraphQL = async ({
await installMissingModules(dbModuleDir, dbMissingResult.missingModules);
}

writePgpmPlan(sqlActionRows as unknown as PgpmRow[], opts);
writePgpmFiles(sqlActionRows as unknown as PgpmRow[], opts);
const filteredRows = filterPlatformLeakage(sqlActionRows as unknown as any[], schema_names);
writePgpmPlan(filteredRows as unknown as PgpmRow[], opts);
writePgpmFiles(filteredRows as unknown as PgpmRow[], opts);
} else {
console.log('No sql_actions found. Skipping database module export.');
}
Expand Down
17 changes: 13 additions & 4 deletions pgpm/export/src/export-migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
installMissingModules,
makeReplacer,
preparePackage,
normalizeOutdir
normalizeOutdir,
filterPlatformLeakage
} from './export-utils';

interface ExportMigrationsToDiskOptions {
Expand Down Expand Up @@ -148,6 +149,14 @@ const exportMigrationsToDisk = async ({
[databaseId]
);

// =========================================================================
// Platform leakage filter — strip platform-integrated actions and
// cross-package requires from the exported package. See
// filterPlatformLeakage() in export-utils.ts for details.
// =========================================================================
const filteredRows = filterPlatformLeakage(results?.rows ?? [], schema_names);


const opts: SqlWriteOptions = {
name,
replacer,
Expand All @@ -158,7 +167,7 @@ const exportMigrationsToDisk = async ({
// Build description for the database extension package
const dbExtensionDesc = extensionDesc || `${name} database schema for ${databaseName}`;

if (results?.rows?.length > 0) {
if (filteredRows.length > 0) {
// Detect missing modules at workspace level and prompt user
const dbMissingResult = await detectMissingModules(project, [...DB_REQUIRED_EXTENSIONS], prompter, argv);

Expand All @@ -180,8 +189,8 @@ const exportMigrationsToDisk = async ({
await installMissingModules(dbModuleDir, dbMissingResult.missingModules);
}

writePgpmPlan(results.rows, opts);
writePgpmFiles(results.rows, opts);
writePgpmPlan(filteredRows, opts);
writePgpmFiles(filteredRows, opts);
} else {
console.log('No sql_actions found — skipping database module. Meta/service module will still be exported.');
}
Expand Down
31 changes: 31 additions & 0 deletions pgpm/export/src/export-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,3 +866,34 @@ export const preparePackage = async ({
export const normalizeOutdir = (outdir: string): string => {
return outdir.endsWith(path.sep) ? outdir : outdir + path.sep;
};

// =============================================================================
// Platform leakage filter
// =============================================================================

/**
* Filters cross-package deps from sql_actions rows before writing them
* as a per-tenant package. Strips deps that reference schemas not owned
* by this database so requires directives only reference tenant-owned
* schemas.
*
* Mirror trigger exclusion is handled upstream via the category mechanism:
* create_trigger_function(v_category := 'platform_mirror') → insert_action
* → depase_ast trigger → db_migrate.sql_actions.category = 'platform_mirror'.
* The server sets export.exclude_categories = 'platform_mirror' (via the
* X-Exclude-Categories header → pgSettings GUC) which activates the
* export_category_filter RLS policy, making those rows invisible at the
* SQL layer before the export code sees them.
*/
export const filterPlatformLeakage = (rows: any[], schema_names: string[]): any[] => {
const ownedPrefixes = schema_names.map(s => `schemas/${s}/`);

return rows.map((row: any) => {
if (Array.isArray(row.deps)) {
row.deps = row.deps.filter((dep: string) =>
ownedPrefixes.some(p => dep.includes(p))
);
}
return row;
});
};
3 changes: 2 additions & 1 deletion pgpm/export/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export {
preparePackage,
normalizeOutdir,
detectMissingModules,
installMissingModules
installMissingModules,
filterPlatformLeakage
} from './export-utils';
export type {
FieldType,
Expand Down
Loading