Skip to content
Merged
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
191 changes: 191 additions & 0 deletions packages/function-resolution/__tests__/default-bucket.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { getConnections, PgTestClient } from 'pgsql-test';

let pg: PgTestClient;
let teardown: () => Promise<void>;

// Deterministic fixture ids.
const PLATFORM_DB = '11111111-1111-1111-1111-111111111111';
const TENANT_DB = '22222222-2222-2222-2222-222222222222';
// A tenant that never labelled a default bucket.
const BARE_DB = '33333333-3333-3333-3333-333333333333';
// A tenant that labelled two, which is a tagging mistake and not a coin flip.
const AMBIGUOUS_DB = '44444444-4444-4444-4444-444444444444';
// A tenant whose own default must never answer somebody else's probe.
const OTHER_DB = '55555555-5555-5555-5555-555555555555';

const ids: Record<string, string> = {};

// The database's default bucket: the answer to "store this file" when no client
// named a bucket. It is a labelled bucket resolved by the same exactly-one rule
// capabilities use — never a literal, never an environment setting, never a
// fallback to another tenant's storage.
describe('default bucket resolution', () => {
beforeAll(async () => {
({ pg, teardown } = await getConnections());

await pg.query(
`INSERT INTO metaschema_public.database (id, name, platform)
VALUES ($1, 'platform_db', true), ($2, 'tenant_db', false),
($3, 'bare_db', false), ($4, 'ambiguous_db', false),
($5, 'other_db', false)`,
[PLATFORM_DB, TENANT_DB, BARE_DB, AMBIGUOUS_DB, OTHER_DB]
);

// The published bucket plane, as the catalog-sync triggers maintain it.
await pg.query(`CREATE SCHEMA catalog_private`);
await pg.query(
`CREATE TABLE catalog_private.buckets (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
owner_scope text NOT NULL,
owner_key uuid,
is_visible boolean NOT NULL DEFAULT false,
database_id uuid NOT NULL,
key text NOT NULL,
type text NOT NULL,
physical_name text,
tags text[]
)`
);

const bucket = async (
database: string,
key: string,
type: string,
tags: string[]
) => {
const row = await pg.one(
`INSERT INTO catalog_private.buckets
(owner_scope, owner_key, is_visible, database_id, key, type, physical_name, tags)
VALUES ('database', $1, false, $1, $2, $3, $4, $5) RETURNING id`,
[database, key, type, `phys-${key}`, tags]
);
return row.id;
};

ids.default = await bucket(TENANT_DB, 'default', 'private', ['default']);
ids.defaultPublic = await bucket(TENANT_DB, 'default-public', 'public', [
'default-public',
]);
ids.avatars = await bucket(TENANT_DB, 'avatars', 'public', ['avatars']);

ids.otherDefault = await bucket(OTHER_DB, 'default', 'private', ['default']);

ids.ambiguousOne = await bucket(AMBIGUOUS_DB, 'documents', 'private', [
'default',
]);
ids.ambiguousTwo = await bucket(AMBIGUOUS_DB, 'uploads', 'private', [
'default',
]);
});

afterAll(async () => {
await teardown();
});

it('the reserved tag vocabulary is one fact, not a literal per call site', async () => {
const [tags] = await pg.any(
`SELECT function_resolution.default_bucket_tag(false) AS private_tag,
function_resolution.default_bucket_tag(true) AS public_tag`
);
expect(tags).toEqual({ private_tag: 'default', public_tag: 'default-public' });
});

it('resolves the database default without anyone naming a bucket', async () => {
const [row] = await pg.any(
`SELECT bucket_id, resolved_key, bucket_type, physical_name, owner_database_id
FROM function_resolution.resolve_default_bucket($1, 'database', $1, false)`,
[TENANT_DB]
);
expect(row).toEqual({
bucket_id: ids.default,
resolved_key: 'default',
bucket_type: 'private',
physical_name: 'phys-default',
owner_database_id: TENANT_DB,
});
});

it('public access resolves the CDN-served default, not the private one', async () => {
const [row] = await pg.any(
`SELECT bucket_id, bucket_type
FROM function_resolution.resolve_default_bucket($1, 'database', $1, true)`,
[TENANT_DB]
);
expect(row).toEqual({ bucket_id: ids.defaultPublic, bucket_type: 'public' });
});

it('an explicit logical key overrides the default and resolves by the same rule', async () => {
const [row] = await pg.any(
`SELECT bucket_id, resolved_key
FROM function_resolution.resolve_default_bucket($1, 'database', $1, false, 'avatars')`,
[TENANT_DB]
);
expect(row).toEqual({ bucket_id: ids.avatars, resolved_key: 'avatars' });
});

it('an explicit key nothing carries raises rather than falling back to the default', async () => {
await expect(
pg.any(
`SELECT * FROM function_resolution.resolve_default_bucket($1, 'database', $1, false, 'nope')`,
[TENANT_DB]
)
).rejects.toThrow(/STORAGE_DEFAULT_BUCKET_NOT_FOUND/);
});

it('a blank key is a caller bug, not a request for the default', async () => {
await expect(
pg.any(
`SELECT * FROM function_resolution.resolve_default_bucket($1, 'database', $1, false, ' ')`,
[TENANT_DB]
)
).rejects.toThrow(/STORAGE_BUCKET_KEY_BLANK/);
});

it('a database with no default bucket raises', async () => {
await expect(
pg.any(
`SELECT * FROM function_resolution.resolve_default_bucket($1, 'database', $1, false)`,
[BARE_DB]
)
).rejects.toThrow(/STORAGE_DEFAULT_BUCKET_NOT_FOUND/);
});

it('another tenant default is never borrowed', async () => {
// OTHER_DB has a bucket tagged 'default'; BARE_DB still has none.
const [foreign] = await pg.any(
`SELECT bucket_id FROM function_resolution.resolve_default_bucket($1, 'database', $1, false)`,
[OTHER_DB]
);
expect(foreign.bucket_id).toBe(ids.otherDefault);

await expect(
pg.any(
`SELECT * FROM function_resolution.resolve_default_bucket($1, 'database', $1, false)`,
[BARE_DB]
)
).rejects.toThrow(/STORAGE_DEFAULT_BUCKET_NOT_FOUND/);
});

it('two default buckets raise, naming the candidates', async () => {
let failure: (Error & { detail?: string }) | null = null;
try {
await pg.any(
`SELECT * FROM function_resolution.resolve_default_bucket($1, 'database', $1, false)`,
[AMBIGUOUS_DB]
);
} catch (error) {
failure = error as Error & { detail?: string };
}

expect(failure).not.toBeNull();
expect(failure!.message).toMatch(/STORAGE_DEFAULT_BUCKET_AMBIGUOUS/);

const detail = JSON.parse(failure!.detail!);
expect(detail.code).toBe('STORAGE_DEFAULT_BUCKET_AMBIGUOUS');
expect(detail.context.tag).toBe('default');
expect(detail.context.candidates).toEqual([
{ bucket_id: ids.ambiguousOne, key: 'documents', type: 'private' },
{ bucket_id: ids.ambiguousTwo, key: 'uploads', type: 'private' },
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
-- Deploy schemas/function_resolution/procedures/bucket_matches to pg

-- requires: schemas/function_resolution/schema
-- requires: schemas/function_resolution/procedures/frame_candidates

BEGIN;

-- bucket_matches: every bucket the nearest answering frame offers for a
-- {tags, type} selector — the lookup half of bucket resolution, with no opinion
-- about how many rows are acceptable.
--
-- Split out so that "which buckets match" has exactly one implementation while
-- each caller states its own arity rule: resolve_bucket demands one for a
-- capability declaration, resolve_default_bucket demands one for a database's
-- default, and both raise on zero or several. A second copy of this query would
-- be a second answer to "which bucket is this".
--
-- One static set-based query answers every frame at once. The shared plane holds
-- every logical database's rows, so each candidate carries the row's expected
-- database_id — the candidate's own key at database scope, the frame's lookup
-- database otherwise. Without it one tenant's probe could be answered by another
-- tenant's row.
--
-- Candidates are probed most-specific frame first and only the nearest frame
-- that answered contributes rows: a nearer frame outranks an outer one, and ties
-- within that frame are the ambiguity a caller raises on.
--
-- Cross-scope reach follows the catalog's own visibility rule: a bucket owned by
-- another database matches only when is_visible (propagated from the source
-- row's is_public), so an outer frame's private bucket is unreachable.
--
-- A frame database without a buckets catalog simply contributes no candidates:
-- storage is an optional module, so its absence is not a provisioning error the
-- way a missing functions catalog is.
--
-- plpgsql, not sql: this module is portable and deploys into databases that host
-- no catalog module, so catalog_private must be resolved on first call rather
-- than at CREATE FUNCTION time.
CREATE FUNCTION function_resolution.bucket_matches(
database_id uuid,
scope text,
entity_id uuid,
tags text[],
type_filter text DEFAULT NULL
) RETURNS TABLE (
bucket_id uuid,
bucket_key text,
bucket_type text,
physical_name text,
owner_database_id uuid,
owner_scope text,
owner_key uuid
) AS $$
BEGIN
RETURN QUERY
WITH hits AS (
SELECT b.id,
b.key,
b.type,
b.physical_name,
b.database_id,
b.owner_scope,
b.owner_key,
cand.ord
FROM function_resolution.frame_candidates(
bucket_matches.database_id,
bucket_matches.scope,
bucket_matches.entity_id
) cand
JOIN catalog_private.buckets b
ON b.owner_scope = cand.owner_scope
AND b.owner_key IS NOT DISTINCT FROM cand.owner_key
AND b.database_id = CASE
WHEN cand.owner_scope = 'database' THEN cand.owner_key
ELSE cand.lookup_database_id
END
WHERE b.tags @> bucket_matches.tags
AND (bucket_matches.type_filter IS NULL OR b.type = bucket_matches.type_filter)
AND (b.database_id = bucket_matches.database_id OR b.is_visible)
)
SELECT h.id,
h.key,
h.type,
h.physical_name,
h.database_id,
h.owner_scope,
h.owner_key
FROM hits h
WHERE h.ord = (SELECT min(hh.ord) FROM hits hh)
ORDER BY h.id;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- Deploy schemas/function_resolution/procedures/default_bucket_tag to pg

-- requires: schemas/function_resolution/schema

BEGIN;

-- default_bucket_tag: the reserved tag vocabulary for "the bucket this database
-- uses when nobody named one", in one place.
--
-- A database's default bucket is not a column, a setting or a flag: it is a
-- bucket a tenant labelled, resolved by the same tag rule capabilities use.
-- Two reserved labels, because "the default" is two questions:
-- default -- the private default (presigned GET)
-- default-public -- the CDN-served default (public reads)
--
-- Reserved only by convention plus provisioning: storage bootstrap applies them,
-- and resolution reads them. A tenant that retags its own buckets changes which
-- bucket is default, and a tenant that applies a label twice gets a loud
-- ambiguity rather than a silent winner.
CREATE FUNCTION function_resolution.default_bucket_tag(
public_access boolean
) RETURNS text AS $$
SELECT CASE WHEN default_bucket_tag.public_access THEN 'default-public' ELSE 'default' END;
$$ LANGUAGE sql IMMUTABLE;

COMMIT;
Loading
Loading