Skip to content

Commit 6ee3b97

Browse files
authored
Merge pull request #1016 from constructive-io/devin/1776687196-grants-policies-node-types
feat!: unify grants[] and policies[] in node type registry and export config
2 parents 4988b64 + acd8ff3 commit 6ee3b97

6 files changed

Lines changed: 72 additions & 83 deletions

File tree

graphql/node-type-registry/src/blueprint-types.generated.ts

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -610,22 +610,22 @@ export interface RelationManyToManyParams {
610610
nodes?: {
611611
[key: string]: unknown;
612612
}[];
613-
/* Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Default: [authenticated] */
614-
grant_roles?: string[];
615-
/* Privilege grants for the junction table as [verb, columns] tuples (e.g. [['select','*'],['insert','*']]). Forwarded to secure_table_provision as-is. Default: select/insert/delete for all columns */
616-
grant_privileges?: string[][];
617-
/* RLS policy type for the junction table. Forwarded to secure_table_provision as-is. NULL means no policy. */
618-
policy_type?: string;
619-
/* Privileges the policy applies to. Forwarded to secure_table_provision as-is. NULL means derived from grant_privileges verbs. */
620-
policy_privileges?: string[];
621-
/* Database role the policy targets. Forwarded to secure_table_provision as-is. NULL means falls back to first grant_role. */
622-
policy_role?: string;
623-
/* Whether the policy is PERMISSIVE (true) or RESTRICTIVE (false). Forwarded to secure_table_provision as-is. */
624-
policy_permissive?: boolean;
625-
/* Policy configuration forwarded to secure_table_provision as-is. Structure varies by policy_type. */
626-
policy_data?: {
627-
[key: string]: unknown;
628-
};
613+
/* Unified grant objects for the junction table. Each entry is { roles: string[], privileges: string[][] }. Forwarded to secure_table_provision as-is. Default: [] */
614+
grants?: {
615+
roles: string[];
616+
privileges: string[][];
617+
}[];
618+
/* RLS policy objects for the junction table. Each entry has $type (Authz* generator), optional data, privileges, policy_role, permissive, policy_name. Forwarded to secure_table_provision as-is. Default: [] */
619+
policies?: {
620+
$type: string;
621+
data?: {
622+
[key: string]: unknown;
623+
};
624+
privileges?: string[];
625+
policy_role?: string;
626+
permissive?: boolean;
627+
policy_name?: string;
628+
}[];
629629
}
630630
/** Declares a spatial predicate between two existing geometry/geography columns. Inserts a metaschema_public.spatial_relation row; the sync_spatial_relation_tags trigger then projects a @spatialRelation smart tag onto the owner column so graphile-postgis' PostgisSpatialRelationsPlugin can expose it as a cross-table filter in GraphQL. Metadata-only: both source_field and target_field must already exist on their tables. Idempotent on (source_table_id, name). One direction per tag — author two RelationSpatial entries if symmetry is desired. */
631631
export interface RelationSpatialParams {
@@ -838,10 +838,11 @@ export interface BlueprintEntityTableProvision {
838838
nodes?: BlueprintNode[];
839839
/** Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is. */
840840
fields?: BlueprintField[];
841-
/** Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is. */
842-
grant_privileges?: unknown[];
843-
/** Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"]. */
844-
grant_roles?: string[];
841+
/** Unified grant objects for the entity table. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples. Forwarded to secure_table_provision as-is. Defaults to []. */
842+
grants?: {
843+
roles: string[];
844+
privileges: unknown[];
845+
}[];
845846
/** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
846847
policies?: BlueprintPolicy[];
847848
}
@@ -1075,10 +1076,11 @@ export interface BlueprintTable {
10751076
fields?: BlueprintField[];
10761077
/** RLS policies for this table. */
10771078
policies?: BlueprintPolicy[];
1078-
/** Database roles to grant privileges to. Defaults to ["authenticated"]. */
1079-
grant_roles?: string[];
1080-
/** Privilege grants as [verb, column] tuples or objects. Defaults to empty (no grants — callers must explicitly specify). */
1081-
grants?: unknown[];
1079+
/** Unified grant objects. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples (e.g. [["select","*"]]). Enables per-role targeting. Defaults to []. */
1080+
grants?: {
1081+
roles: string[];
1082+
privileges: unknown[];
1083+
}[];
10821084
/** Whether to enable RLS on this table. Defaults to true. */
10831085
use_rls?: boolean;
10841086
/** Table-level indexes (table_name inherited from parent). */

graphql/node-type-registry/src/codegen/generate-types.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -642,12 +642,16 @@ function buildBlueprintEntityTableProvision(): t.ExportNamedDeclaration {
642642
'Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is.'
643643
),
644644
addJSDoc(
645-
optionalProp('grant_privileges', t.tsArrayType(t.tsUnknownKeyword())),
646-
'Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is.'
647-
),
648-
addJSDoc(
649-
optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())),
650-
'Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"].'
645+
optionalProp(
646+
'grants',
647+
t.tsArrayType(
648+
t.tsTypeLiteral([
649+
requiredProp('roles', t.tsArrayType(t.tsStringKeyword())),
650+
requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword())),
651+
])
652+
)
653+
),
654+
'Unified grant objects for the entity table. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples. Forwarded to secure_table_provision as-is. Defaults to [].'
651655
),
652656
addJSDoc(
653657
optionalProp(
@@ -749,12 +753,16 @@ function buildBlueprintTable(): t.ExportNamedDeclaration {
749753
'RLS policies for this table.'
750754
),
751755
addJSDoc(
752-
optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())),
753-
'Database roles to grant privileges to. Defaults to ["authenticated"].'
754-
),
755-
addJSDoc(
756-
optionalProp('grants', t.tsArrayType(t.tsUnknownKeyword())),
757-
'Privilege grants as [verb, column] tuples or objects. Defaults to empty (no grants — callers must explicitly specify).'
756+
optionalProp(
757+
'grants',
758+
t.tsArrayType(
759+
t.tsTypeLiteral([
760+
requiredProp('roles', t.tsArrayType(t.tsStringKeyword())),
761+
requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword())),
762+
])
763+
)
764+
),
765+
'Unified grant objects. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples (e.g. [["select","*"]]). Enables per-role targeting. Defaults to [].'
758766
),
759767
addJSDoc(
760768
optionalProp('use_rls', t.tsBooleanKeyword()),

graphql/node-type-registry/src/relation/relation-many-to-many.ts

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,46 +48,33 @@ export const RelationManyToMany: NodeTypeDefinition = {
4848
},
4949
"description": "Array of node objects for field creation on junction table. Each object has a $type key (e.g. DataId, DataEntityMembership) and optional data keys. Forwarded to secure_table_provision as-is. Empty array means no additional fields."
5050
},
51-
"grant_roles": {
51+
"grants": {
5252
"type": "array",
5353
"items": {
54-
"type": "string"
54+
"type": "object",
55+
"properties": {
56+
"roles": { "type": "array", "items": { "type": "string" } },
57+
"privileges": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }
58+
},
59+
"required": ["roles", "privileges"]
5560
},
56-
"description": "Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Default: [authenticated]"
61+
"description": "Unified grant objects for the junction table. Each entry is { roles: string[], privileges: string[][] }. Forwarded to secure_table_provision as-is. Default: []"
5762
},
58-
"grant_privileges": {
63+
"policies": {
5964
"type": "array",
6065
"items": {
61-
"type": "array",
62-
"items": {
63-
"type": "string"
64-
}
66+
"type": "object",
67+
"properties": {
68+
"$type": { "type": "string" },
69+
"data": { "type": "object" },
70+
"privileges": { "type": "array", "items": { "type": "string" } },
71+
"policy_role": { "type": "string" },
72+
"permissive": { "type": "boolean" },
73+
"policy_name": { "type": "string" }
74+
},
75+
"required": ["$type"]
6576
},
66-
"description": "Privilege grants for the junction table as [verb, columns] tuples (e.g. [['select','*'],['insert','*']]). Forwarded to secure_table_provision as-is. Default: select/insert/delete for all columns"
67-
},
68-
"policy_type": {
69-
"type": "string",
70-
"description": "RLS policy type for the junction table. Forwarded to secure_table_provision as-is. NULL means no policy."
71-
},
72-
"policy_privileges": {
73-
"type": "array",
74-
"items": {
75-
"type": "string"
76-
},
77-
"description": "Privileges the policy applies to. Forwarded to secure_table_provision as-is. NULL means derived from grant_privileges verbs."
78-
},
79-
"policy_role": {
80-
"type": "string",
81-
"description": "Database role the policy targets. Forwarded to secure_table_provision as-is. NULL means falls back to first grant_role."
82-
},
83-
"policy_permissive": {
84-
"type": "boolean",
85-
"description": "Whether the policy is PERMISSIVE (true) or RESTRICTIVE (false). Forwarded to secure_table_provision as-is.",
86-
"default": true
87-
},
88-
"policy_data": {
89-
"type": "object",
90-
"description": "Policy configuration forwarded to secure_table_provision as-is. Structure varies by policy_type."
77+
"description": "RLS policy objects for the junction table. Each entry has $type (Authz* generator), optional data, privileges, policy_role, permissive, policy_name. Forwarded to secure_table_provision as-is. Default: []"
9178
}
9279
},
9380
"required": [

packages/csv-to-pg/__tests__/__snapshots__/export.test.ts.snap

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ exports[`test case empty array fields emit empty array literal 1`] = `
1313
id,
1414
node_type,
1515
fields,
16-
grant_roles
16+
out_fields
1717
) VALUES
1818
('450e3b3b-b68d-4abc-990c-65cb8a1dcdb4', 'DataTimestamps', '{}', '{}');"
1919
`;
@@ -76,10 +76,9 @@ exports[`test case null array fields emit empty array literal instead of NULL 1`
7676
id,
7777
node_type,
7878
fields,
79-
grant_privileges,
8079
out_fields
8180
) VALUES
82-
('450e3b3b-b68d-4abc-990c-65cb8a1dcdb4', 'DataTimestamps', '{}', '{}', '{}');"
81+
('450e3b3b-b68d-4abc-990c-65cb8a1dcdb4', 'DataTimestamps', '{}', '{}');"
8382
`;
8483

8584
exports[`test case test case 1`] = `Promise {}`;

packages/csv-to-pg/__tests__/export.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,6 @@ it('null array fields emit empty array literal instead of NULL', async () => {
229229
id: 'uuid',
230230
node_type: 'text',
231231
fields: 'jsonb[]',
232-
grant_privileges: 'jsonb[]',
233232
out_fields: 'uuid[]'
234233
}
235234
});
@@ -239,7 +238,6 @@ it('null array fields emit empty array literal instead of NULL', async () => {
239238
id: '450e3b3b-b68d-4abc-990c-65cb8a1dcdb4',
240239
node_type: 'DataTimestamps',
241240
fields: null,
242-
grant_privileges: null,
243241
out_fields: null
244242
}
245243
]);
@@ -259,7 +257,7 @@ it('empty array fields emit empty array literal', async () => {
259257
id: 'uuid',
260258
node_type: 'text',
261259
fields: 'jsonb[]',
262-
grant_roles: 'text[]'
260+
out_fields: 'uuid[]'
263261
}
264262
});
265263

@@ -268,7 +266,7 @@ it('empty array fields emit empty array literal', async () => {
268266
id: '450e3b3b-b68d-4abc-990c-65cb8a1dcdb4',
269267
node_type: 'DataTimestamps',
270268
fields: [],
271-
grant_roles: []
269+
out_fields: []
272270
}
273271
]);
274272

pgpm/export/src/export-utils.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -985,14 +985,9 @@ export const META_TABLE_CONFIG: Record<string, TableConfig> = {
985985
node_type: 'text',
986986
use_rls: 'boolean',
987987
node_data: 'jsonb',
988-
grant_roles: 'text[]',
989988
fields: 'jsonb[]',
990-
grant_privileges: 'jsonb[]',
991-
policy_type: 'text',
992-
policy_privileges: 'text[]',
993-
policy_role: 'text',
994-
policy_permissive: 'boolean',
995-
policy_data: 'jsonb',
989+
grants: 'jsonb',
990+
policies: 'jsonb',
996991
out_fields: 'uuid[]'
997992
}
998993
},

0 commit comments

Comments
 (0)