diff --git a/controlplane/migrations/0108_dazzling_iron_lad.sql b/controlplane/migrations/0108_dazzling_iron_lad.sql new file mode 100644 index 0000000000..770b661564 --- /dev/null +++ b/controlplane/migrations/0108_dazzling_iron_lad.sql @@ -0,0 +1,7 @@ +ALTER TABLE "subgraphs" DROP CONSTRAINT "subgraphs_schema_version_id_schema_versions_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "subgraphs" ADD CONSTRAINT "subgraphs_schema_version_id_schema_versions_id_fk" FOREIGN KEY ("schema_version_id") REFERENCES "public"."schema_versions"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/controlplane/migrations/meta/0108_snapshot.json b/controlplane/migrations/meta/0108_snapshot.json new file mode 100644 index 0000000000..28b2f312e3 --- /dev/null +++ b/controlplane/migrations/meta/0108_snapshot.json @@ -0,0 +1,6712 @@ +{ + "id": "63d22ed0-c00e-4b16-b1bc-a8be665f9732", + "prevId": "33936158-f713-4278-98aa-88f9f902baae", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.api_key_permissions": { + "name": "api_key_permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "api_key_id": { + "name": "api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "akp_api_key_id_idx": { + "name": "akp_api_key_id_idx", + "columns": [ + { + "expression": "api_key_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_key_permissions_api_key_id_api_keys_id_fk": { + "name": "api_key_permissions_api_key_id_api_keys_id_fk", + "tableFrom": "api_key_permissions", + "tableTo": "api_keys", + "columnsFrom": [ + "api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.api_key_resources": { + "name": "api_key_resources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "api_key_id": { + "name": "api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "akr_api_key_id_idx": { + "name": "akr_api_key_id_idx", + "columns": [ + { + "expression": "api_key_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "akr_target_id_idx": { + "name": "akr_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_key_resources_api_key_id_api_keys_id_fk": { + "name": "api_key_resources_api_key_id_api_keys_id_fk", + "tableFrom": "api_key_resources", + "tableTo": "api_keys", + "columnsFrom": [ + "api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_resources_target_id_targets_id_fk": { + "name": "api_key_resources_target_id_targets_id_fk", + "tableFrom": "api_key_resources", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "apikey_name_idx": { + "name": "apikey_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ak_user_id_idx": { + "name": "ak_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ak_organization_id_idx": { + "name": "ak_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_keys_organization_id_organizations_id_fk": { + "name": "api_keys_organization_id_organizations_id_fk", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "checkConstraints": {} + }, + "public.audit_logs": { + "name": "audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "audit_action": { + "name": "audit_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auditable_type": { + "name": "auditable_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auditable_display_name": { + "name": "auditable_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_display_name": { + "name": "target_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_namespace_id": { + "name": "target_namespace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_namespace": { + "name": "target_namespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_display_name": { + "name": "actor_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auditlogs_organization_idx": { + "name": "auditlogs_organization_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auditlogs_created_at_idx": { + "name": "auditlogs_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_logs_organization_id_organizations_id_fk": { + "name": "audit_logs_organization_id_organizations_id_fk", + "tableFrom": "audit_logs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.billing_plans": { + "name": "billing_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "features": { + "name": "features", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "stripe_price_id": { + "name": "stripe_price_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billsubs_organization_id_idx": { + "name": "billsubs_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_subscriptions_organization_id_organizations_id_fk": { + "name": "billing_subscriptions_organization_id_organizations_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.contracts": { + "name": "contracts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "source_federated_graph_id": { + "name": "source_federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "downstream_federated_graph_id": { + "name": "downstream_federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "exclude_tags": { + "name": "exclude_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "include_tags": { + "name": "include_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_id": { + "name": "updated_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "contracts_created_by_id_idx": { + "name": "contracts_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contracts_updated_by_id_idx": { + "name": "contracts_updated_by_id_idx", + "columns": [ + { + "expression": "updated_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contracts_downstream_federated_graph_id_idx": { + "name": "contracts_downstream_federated_graph_id_idx", + "columns": [ + { + "expression": "downstream_federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contracts_source_federated_graph_id_federated_graphs_id_fk": { + "name": "contracts_source_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "contracts", + "tableTo": "federated_graphs", + "columnsFrom": [ + "source_federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "contracts_downstream_federated_graph_id_federated_graphs_id_fk": { + "name": "contracts_downstream_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "contracts", + "tableTo": "federated_graphs", + "columnsFrom": [ + "downstream_federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "contracts_created_by_id_users_id_fk": { + "name": "contracts_created_by_id_users_id_fk", + "tableFrom": "contracts", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "contracts_updated_by_id_users_id_fk": { + "name": "contracts_updated_by_id_users_id_fk", + "tableFrom": "contracts", + "tableTo": "users", + "columnsFrom": [ + "updated_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "federated_graph_source_downstream_id": { + "name": "federated_graph_source_downstream_id", + "nullsNotDistinct": false, + "columns": [ + "source_federated_graph_id", + "downstream_federated_graph_id" + ] + } + }, + "checkConstraints": {} + }, + "public.discussion_thread": { + "name": "discussion_thread", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "discussion_id": { + "name": "discussion_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "content_markdown": { + "name": "content_markdown", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_json": { + "name": "content_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "dist_discussion_id_idx": { + "name": "dist_discussion_id_idx", + "columns": [ + { + "expression": "discussion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dist_created_by_id_idx": { + "name": "dist_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "discussion_thread_discussion_id_discussions_id_fk": { + "name": "discussion_thread_discussion_id_discussions_id_fk", + "tableFrom": "discussion_thread", + "tableTo": "discussions", + "columnsFrom": [ + "discussion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "discussion_thread_created_by_id_users_id_fk": { + "name": "discussion_thread_created_by_id_users_id_fk", + "tableFrom": "discussion_thread", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.discussions": { + "name": "discussions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reference_line": { + "name": "reference_line", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_resolved": { + "name": "is_resolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "dis_target_id_idx": { + "name": "dis_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dis_schema_version_id_idx": { + "name": "dis_schema_version_id_idx", + "columns": [ + { + "expression": "schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "discussions_target_id_targets_id_fk": { + "name": "discussions_target_id_targets_id_fk", + "tableFrom": "discussions", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "discussions_schema_version_id_schema_versions_id_fk": { + "name": "discussions_schema_version_id_schema_versions_id_fk", + "tableFrom": "discussions", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.feature_flags_to_feature_subgraphs": { + "name": "feature_flags_to_feature_subgraphs", + "schema": "", + "columns": { + "feature_flag_id": { + "name": "feature_flag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feature_subgraph_id": { + "name": "feature_subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "fffs_feature_flag_id_idx": { + "name": "fffs_feature_flag_id_idx", + "columns": [ + { + "expression": "feature_flag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fffs_feature_subgraph_id_idx": { + "name": "fffs_feature_subgraph_id_idx", + "columns": [ + { + "expression": "feature_subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "feature_flags_to_feature_subgraphs_feature_flag_id_feature_flags_id_fk": { + "name": "feature_flags_to_feature_subgraphs_feature_flag_id_feature_flags_id_fk", + "tableFrom": "feature_flags_to_feature_subgraphs", + "tableTo": "feature_flags", + "columnsFrom": [ + "feature_flag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feature_flags_to_feature_subgraphs_feature_subgraph_id_subgraphs_id_fk": { + "name": "feature_flags_to_feature_subgraphs_feature_subgraph_id_subgraphs_id_fk", + "tableFrom": "feature_flags_to_feature_subgraphs", + "tableTo": "subgraphs", + "columnsFrom": [ + "feature_subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "feature_flags_to_feature_subgraphs_feature_flag_id_feature_subgraph_id_pk": { + "name": "feature_flags_to_feature_subgraphs_feature_flag_id_feature_subgraph_id_pk", + "columns": [ + "feature_flag_id", + "feature_subgraph_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.feature_flags": { + "name": "feature_flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "namespace_id": { + "name": "namespace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "labels": { + "name": "labels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ff_organization_id_idx": { + "name": "ff_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ff_namespace_id_idx": { + "name": "ff_namespace_id_idx", + "columns": [ + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ff_created_by_idx": { + "name": "ff_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "feature_flags_organization_id_organizations_id_fk": { + "name": "feature_flags_organization_id_organizations_id_fk", + "tableFrom": "feature_flags", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feature_flags_namespace_id_namespaces_id_fk": { + "name": "feature_flags_namespace_id_namespaces_id_fk", + "tableFrom": "feature_flags", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feature_flags_created_by_users_id_fk": { + "name": "feature_flags_created_by_users_id_fk", + "tableFrom": "feature_flags", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.feature_subgraphs_to_base_subgraphs": { + "name": "feature_subgraphs_to_base_subgraphs", + "schema": "", + "columns": { + "feature_subgraph_id": { + "name": "feature_subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "base_subgraph_id": { + "name": "base_subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "fsbs_feature_subgraph_id_idx": { + "name": "fsbs_feature_subgraph_id_idx", + "columns": [ + { + "expression": "feature_subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fsbs_base_subgraph_id_idx": { + "name": "fsbs_base_subgraph_id_idx", + "columns": [ + { + "expression": "base_subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "feature_subgraphs_to_base_subgraphs_feature_subgraph_id_subgraphs_id_fk": { + "name": "feature_subgraphs_to_base_subgraphs_feature_subgraph_id_subgraphs_id_fk", + "tableFrom": "feature_subgraphs_to_base_subgraphs", + "tableTo": "subgraphs", + "columnsFrom": [ + "feature_subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feature_subgraphs_to_base_subgraphs_base_subgraph_id_subgraphs_id_fk": { + "name": "feature_subgraphs_to_base_subgraphs_base_subgraph_id_subgraphs_id_fk", + "tableFrom": "feature_subgraphs_to_base_subgraphs", + "tableTo": "subgraphs", + "columnsFrom": [ + "base_subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "feature_subgraphs_to_base_subgraphs_feature_subgraph_id_base_subgraph_id_pk": { + "name": "feature_subgraphs_to_base_subgraphs_feature_subgraph_id_base_subgraph_id_pk", + "columns": [ + "feature_subgraph_id", + "base_subgraph_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.federated_graph_clients": { + "name": "federated_graph_clients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_id": { + "name": "updated_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "fgc_created_by_id_idx": { + "name": "fgc_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgc_updated_by_id_idx": { + "name": "fgc_updated_by_id_idx", + "columns": [ + { + "expression": "updated_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "federated_graph_clients_federated_graph_id_federated_graphs_id_fk": { + "name": "federated_graph_clients_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "federated_graph_clients", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graph_clients_created_by_id_users_id_fk": { + "name": "federated_graph_clients_created_by_id_users_id_fk", + "tableFrom": "federated_graph_clients", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "federated_graph_clients_updated_by_id_users_id_fk": { + "name": "federated_graph_clients_updated_by_id_users_id_fk", + "tableFrom": "federated_graph_clients", + "tableTo": "users", + "columnsFrom": [ + "updated_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "federated_graph_client_name": { + "name": "federated_graph_client_name", + "nullsNotDistinct": false, + "columns": [ + "federated_graph_id", + "name" + ] + } + }, + "checkConstraints": {} + }, + "public.federated_graph_persisted_operations": { + "name": "federated_graph_persisted_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "operation_id": { + "name": "operation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operation_names": { + "name": "operation_names", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "operation_content": { + "name": "operation_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "updated_by_id": { + "name": "updated_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "fgpo_created_by_id_idx": { + "name": "fgpo_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgpo_updated_by_id_idx": { + "name": "fgpo_updated_by_id_idx", + "columns": [ + { + "expression": "updated_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgpo_client_id_idx": { + "name": "fgpo_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "federated_graph_persisted_operations_federated_graph_id_federated_graphs_id_fk": { + "name": "federated_graph_persisted_operations_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "federated_graph_persisted_operations", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graph_persisted_operations_client_id_federated_graph_clients_id_fk": { + "name": "federated_graph_persisted_operations_client_id_federated_graph_clients_id_fk", + "tableFrom": "federated_graph_persisted_operations", + "tableTo": "federated_graph_clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graph_persisted_operations_created_by_id_users_id_fk": { + "name": "federated_graph_persisted_operations_created_by_id_users_id_fk", + "tableFrom": "federated_graph_persisted_operations", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "federated_graph_persisted_operations_updated_by_id_users_id_fk": { + "name": "federated_graph_persisted_operations_updated_by_id_users_id_fk", + "tableFrom": "federated_graph_persisted_operations", + "tableTo": "users", + "columnsFrom": [ + "updated_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "federated_graph_persisted_operations_file_path_unique": { + "name": "federated_graph_persisted_operations_file_path_unique", + "nullsNotDistinct": false, + "columns": [ + "file_path" + ] + }, + "federated_graph_operation_id": { + "name": "federated_graph_operation_id", + "nullsNotDistinct": false, + "columns": [ + "federated_graph_id", + "client_id", + "operation_id" + ] + } + }, + "checkConstraints": {} + }, + "public.federated_graphs": { + "name": "federated_graphs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "routing_url": { + "name": "routing_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "composed_schema_version_id": { + "name": "composed_schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "admission_webhook_url": { + "name": "admission_webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "admission_webhook_secret": { + "name": "admission_webhook_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "supports_federation": { + "name": "supports_federation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "fgs_target_id_idx": { + "name": "fgs_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgs_composed_schema_version_id_idx": { + "name": "fgs_composed_schema_version_id_idx", + "columns": [ + { + "expression": "composed_schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "federated_graphs_target_id_targets_id_fk": { + "name": "federated_graphs_target_id_targets_id_fk", + "tableFrom": "federated_graphs", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graphs_composed_schema_version_id_schema_versions_id_fk": { + "name": "federated_graphs_composed_schema_version_id_schema_versions_id_fk", + "tableFrom": "federated_graphs", + "tableTo": "schema_versions", + "columnsFrom": [ + "composed_schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.federated_graphs_to_feature_flag_schema_versions": { + "name": "federated_graphs_to_feature_flag_schema_versions", + "schema": "", + "columns": { + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "base_composition_schema_version_id": { + "name": "base_composition_schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "composed_schema_version_id": { + "name": "composed_schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feature_flag_id": { + "name": "feature_flag_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "fgffsv_federated_graph_id_idx": { + "name": "fgffsv_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgffsv_base_composition_schema_version_id_idx": { + "name": "fgffsv_base_composition_schema_version_id_idx", + "columns": [ + { + "expression": "base_composition_schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgffsv_composed_schema_version_id_idx": { + "name": "fgffsv_composed_schema_version_id_idx", + "columns": [ + { + "expression": "composed_schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgffsv_feature_flag_id_idx": { + "name": "fgffsv_feature_flag_id_idx", + "columns": [ + { + "expression": "feature_flag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "federated_graphs_to_feature_flag_schema_versions_federated_graph_id_federated_graphs_id_fk": { + "name": "federated_graphs_to_feature_flag_schema_versions_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "federated_graphs_to_feature_flag_schema_versions", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graphs_to_feature_flag_schema_versions_base_composition_schema_version_id_schema_versions_id_fk": { + "name": "federated_graphs_to_feature_flag_schema_versions_base_composition_schema_version_id_schema_versions_id_fk", + "tableFrom": "federated_graphs_to_feature_flag_schema_versions", + "tableTo": "schema_versions", + "columnsFrom": [ + "base_composition_schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graphs_to_feature_flag_schema_versions_composed_schema_version_id_schema_versions_id_fk": { + "name": "federated_graphs_to_feature_flag_schema_versions_composed_schema_version_id_schema_versions_id_fk", + "tableFrom": "federated_graphs_to_feature_flag_schema_versions", + "tableTo": "schema_versions", + "columnsFrom": [ + "composed_schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graphs_to_feature_flag_schema_versions_feature_flag_id_feature_flags_id_fk": { + "name": "federated_graphs_to_feature_flag_schema_versions_feature_flag_id_feature_flags_id_fk", + "tableFrom": "federated_graphs_to_feature_flag_schema_versions", + "tableTo": "feature_flags", + "columnsFrom": [ + "feature_flag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "federated_graphs_to_feature_flag_schema_versions_federated_graph_id_base_composition_schema_version_id_composed_schema_version_id_pk": { + "name": "federated_graphs_to_feature_flag_schema_versions_federated_graph_id_base_composition_schema_version_id_composed_schema_version_id_pk", + "columns": [ + "federated_graph_id", + "base_composition_schema_version_id", + "composed_schema_version_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.field_grace_period": { + "name": "field_grace_period", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subgraph_id": { + "name": "subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "namespace_id": { + "name": "namespace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "is_deprecated": { + "name": "is_deprecated", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "unique_field_grace_period_idx": { + "name": "unique_field_grace_period_idx", + "columns": [ + { + "expression": "subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_deprecated", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgp_subgraph_id_idx": { + "name": "fgp_subgraph_id_idx", + "columns": [ + { + "expression": "subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgp_organization_id_idx": { + "name": "fgp_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fgp_namespace_id_idx": { + "name": "fgp_namespace_id_idx", + "columns": [ + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "field_grace_period_subgraph_id_subgraphs_id_fk": { + "name": "field_grace_period_subgraph_id_subgraphs_id_fk", + "tableFrom": "field_grace_period", + "tableTo": "subgraphs", + "columnsFrom": [ + "subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "field_grace_period_organization_id_organizations_id_fk": { + "name": "field_grace_period_organization_id_organizations_id_fk", + "tableFrom": "field_grace_period", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "field_grace_period_namespace_id_namespaces_id_fk": { + "name": "field_grace_period_namespace_id_namespaces_id_fk", + "tableFrom": "field_grace_period", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.git_installations": { + "name": "git_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "git_installation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider_installation_id": { + "name": "provider_installation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider_name": { + "name": "provider_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_token": { + "name": "oauth_token", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.graph_api_tokens": { + "name": "graph_api_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "graphApiToken_name_idx": { + "name": "graphApiToken_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gat_organization_id_idx": { + "name": "gat_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gat_federated_graph_id_idx": { + "name": "gat_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gat_created_by_idx": { + "name": "gat_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "graph_api_tokens_organization_id_organizations_id_fk": { + "name": "graph_api_tokens_organization_id_organizations_id_fk", + "tableFrom": "graph_api_tokens", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "graph_api_tokens_federated_graph_id_federated_graphs_id_fk": { + "name": "graph_api_tokens_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "graph_api_tokens", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "graph_api_tokens_created_by_users_id_fk": { + "name": "graph_api_tokens_created_by_users_id_fk", + "tableFrom": "graph_api_tokens", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "graph_api_tokens_token_unique": { + "name": "graph_api_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "checkConstraints": {} + }, + "public.graph_composition_subgraphs": { + "name": "graph_composition_subgraphs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "graph_composition_id": { + "name": "graph_composition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_id": { + "name": "subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_target_id": { + "name": "subgraph_target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_name": { + "name": "subgraph_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "graph_composition_subgraph_change_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unchanged'" + }, + "is_feature_subgraph": { + "name": "is_feature_subgraph", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "graphcompsub_graph_composition_id_idx": { + "name": "graphcompsub_graph_composition_id_idx", + "columns": [ + { + "expression": "graph_composition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "graphcompsub_schema_version_id_idx": { + "name": "graphcompsub_schema_version_id_idx", + "columns": [ + { + "expression": "schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "graph_composition_subgraphs_graph_composition_id_graph_compositions_id_fk": { + "name": "graph_composition_subgraphs_graph_composition_id_graph_compositions_id_fk", + "tableFrom": "graph_composition_subgraphs", + "tableTo": "graph_compositions", + "columnsFrom": [ + "graph_composition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "graph_composition_subgraphs_schema_version_id_schema_versions_id_fk": { + "name": "graph_composition_subgraphs_schema_version_id_schema_versions_id_fk", + "tableFrom": "graph_composition_subgraphs", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.graph_compositions": { + "name": "graph_compositions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_composable": { + "name": "is_composable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "composition_errors": { + "name": "composition_errors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composition_warnings": { + "name": "composition_warnings", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "router_config_signature": { + "name": "router_config_signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_error": { + "name": "deployment_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "admission_error": { + "name": "admission_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_email": { + "name": "created_by_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_feature_flag_composition": { + "name": "is_feature_flag_composition", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "graphcomp_schema_version_id_idx": { + "name": "graphcomp_schema_version_id_idx", + "columns": [ + { + "expression": "schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "graphcomp_created_by_id_idx": { + "name": "graphcomp_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "graph_compositions_schema_version_id_schema_versions_id_fk": { + "name": "graph_compositions_schema_version_id_schema_versions_id_fk", + "tableFrom": "graph_compositions", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "graph_compositions_created_by_id_users_id_fk": { + "name": "graph_compositions_created_by_id_users_id_fk", + "tableFrom": "graph_compositions", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.graph_request_keys": { + "name": "graph_request_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "grk_organization_id_idx": { + "name": "grk_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "grk_federated_graph_id_idx": { + "name": "grk_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "graph_request_keys_organization_id_organizations_id_fk": { + "name": "graph_request_keys_organization_id_organizations_id_fk", + "tableFrom": "graph_request_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "graph_request_keys_federated_graph_id_federated_graphs_id_fk": { + "name": "graph_request_keys_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "graph_request_keys", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "graph_request_keys_federated_graph_id_unique": { + "name": "graph_request_keys_federated_graph_id_unique", + "nullsNotDistinct": false, + "columns": [ + "federated_graph_id" + ] + }, + "graph_request_keys_privateKey_unique": { + "name": "graph_request_keys_privateKey_unique", + "nullsNotDistinct": false, + "columns": [ + "privateKey" + ] + }, + "graph_request_keys_publicKey_unique": { + "name": "graph_request_keys_publicKey_unique", + "nullsNotDistinct": false, + "columns": [ + "publicKey" + ] + } + }, + "checkConstraints": {} + }, + "public.namespace_graph_pruning_check_config": { + "name": "namespace_graph_pruning_check_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "namespace_id": { + "name": "namespace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "graph_pruning_rule": { + "name": "graph_pruning_rule", + "type": "graph_pruning_rules", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "severity_level": { + "name": "severity_level", + "type": "lint_severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "grace_period": { + "name": "grace_period", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheme_usage_check_period": { + "name": "scheme_usage_check_period", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "nsgpcc_namespace_id_idx": { + "name": "nsgpcc_namespace_id_idx", + "columns": [ + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespace_graph_pruning_check_config_namespace_id_namespaces_id_fk": { + "name": "namespace_graph_pruning_check_config_namespace_id_namespaces_id_fk", + "tableFrom": "namespace_graph_pruning_check_config", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.namespace_lint_check_config": { + "name": "namespace_lint_check_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "namespace_id": { + "name": "namespace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "lint_rule": { + "name": "lint_rule", + "type": "lint_rules", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "severity_level": { + "name": "severity_level", + "type": "lint_severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "nslcc_namespace_id_idx": { + "name": "nslcc_namespace_id_idx", + "columns": [ + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespace_lint_check_config_namespace_id_namespaces_id_fk": { + "name": "namespace_lint_check_config_namespace_id_namespaces_id_fk", + "tableFrom": "namespace_lint_check_config", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.namespaces": { + "name": "namespaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "enable_linting": { + "name": "enable_linting", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enable_graph_pruning": { + "name": "enable_graph_pruning", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "ns_organization_id_idx": { + "name": "ns_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ns_created_by_idx": { + "name": "ns_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespaces_organization_id_organizations_id_fk": { + "name": "namespaces_organization_id_organizations_id_fk", + "tableFrom": "namespaces", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "namespaces_created_by_users_id_fk": { + "name": "namespaces_created_by_users_id_fk", + "tableFrom": "namespaces", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_name": { + "name": "unique_name", + "nullsNotDistinct": false, + "columns": [ + "name", + "organization_id" + ] + } + }, + "checkConstraints": {} + }, + "public.oidc_providers": { + "name": "oidc_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alias": { + "name": "alias", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oidcp_organization_id_idx": { + "name": "oidcp_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oidc_providers_organization_id_organizations_id_fk": { + "name": "oidc_providers_organization_id_organizations_id_fk", + "tableFrom": "oidc_providers", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oidc_providers_alias_unique": { + "name": "oidc_providers_alias_unique", + "nullsNotDistinct": false, + "columns": [ + "alias" + ] + } + }, + "checkConstraints": {} + }, + "public.operation_change_overrides": { + "name": "operation_change_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_id": { + "name": "namespace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "schema_change_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "hash_change_idx": { + "name": "hash_change_idx", + "columns": [ + { + "expression": "hash", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "change_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oco_created_by_idx": { + "name": "oco_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "operation_change_overrides_created_by_users_id_fk": { + "name": "operation_change_overrides_created_by_users_id_fk", + "tableFrom": "operation_change_overrides", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.operation_ignore_all_overrides": { + "name": "operation_ignore_all_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "namespace_id": { + "name": "namespace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "hash_namespace_ignore_idx": { + "name": "hash_namespace_ignore_idx", + "columns": [ + { + "expression": "hash", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oiao_created_by_idx": { + "name": "oiao_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "operation_ignore_all_overrides_created_by_users_id_fk": { + "name": "operation_ignore_all_overrides_created_by_users_id_fk", + "tableFrom": "operation_ignore_all_overrides", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_billing": { + "name": "organization_billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "organization_billing_idx": { + "name": "organization_billing_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_billing_stripe_idx": { + "name": "organization_billing_stripe_idx", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_billing_organization_id_organizations_id_fk": { + "name": "organization_billing_organization_id_organizations_id_fk", + "tableFrom": "organization_billing", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_features": { + "name": "organization_features", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feature": { + "name": "feature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "limit": { + "name": "limit", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "organization_feature_idx": { + "name": "organization_feature_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "feature", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orgf_organization_id_idx": { + "name": "orgf_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_features_organization_id_organizations_id_fk": { + "name": "organization_features_organization_id_organizations_id_fk", + "tableFrom": "organization_features", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_integrations": { + "name": "organization_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "events": { + "name": "events", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "integration_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_integration_idx": { + "name": "organization_integration_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orgint_organization_id_idx": { + "name": "orgint_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_integrations_organization_id_organizations_id_fk": { + "name": "organization_integrations_organization_id_organizations_id_fk", + "tableFrom": "organization_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_invitations": { + "name": "organization_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "accepted": { + "name": "accepted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "orginv_organization_id_idx": { + "name": "orginv_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orginv_user_id_idx": { + "name": "orginv_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orginv_invited_by_idx": { + "name": "orginv_invited_by_idx", + "columns": [ + { + "expression": "invited_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_invitations_organization_id_organizations_id_fk": { + "name": "organization_invitations_organization_id_organizations_id_fk", + "tableFrom": "organization_invitations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_invitations_user_id_users_id_fk": { + "name": "organization_invitations_user_id_users_id_fk", + "tableFrom": "organization_invitations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_invitations_invited_by_users_id_fk": { + "name": "organization_invitations_invited_by_users_id_fk", + "tableFrom": "organization_invitations", + "tableTo": "users", + "columnsFrom": [ + "invited_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_member_roles": { + "name": "organization_member_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_member_id": { + "name": "organization_member_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "member_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "organization_member_role_idx": { + "name": "organization_member_role_idx", + "columns": [ + { + "expression": "organization_member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_member_roles_organization_member_id_organization_members_id_fk": { + "name": "organization_member_roles_organization_member_id_organization_members_id_fk", + "tableFrom": "organization_member_roles", + "tableTo": "organization_members", + "columnsFrom": [ + "organization_member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organization_webhook_configs": { + "name": "organization_webhook_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "events": { + "name": "events", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "orgwc_organization_id_idx": { + "name": "orgwc_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_webhook_configs_organization_id_organizations_id_fk": { + "name": "organization_webhook_configs_organization_id_organizations_id_fk", + "tableFrom": "organization_webhook_configs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_deactivated": { + "name": "is_deactivated", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "deactivation_reason": { + "name": "deactivation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deactivated_at": { + "name": "deactivated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "orgs_created_by_idx": { + "name": "orgs_created_by_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "checkConstraints": {} + }, + "public.organization_members": { + "name": "organization_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_member_idx": { + "name": "organization_member_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "unique_organization_member_idx": { + "name": "unique_organization_member_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orgm_organization_id_idx": { + "name": "orgm_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.playground_scripts": { + "name": "playground_scripts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "type": { + "name": "type", + "type": "playground_script_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "ps_organization_id_idx": { + "name": "ps_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ps_created_by_id_idx": { + "name": "ps_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "playground_scripts_organization_id_organizations_id_fk": { + "name": "playground_scripts_organization_id_organizations_id_fk", + "tableFrom": "playground_scripts", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playground_scripts_created_by_id_users_id_fk": { + "name": "playground_scripts_created_by_id_users_id_fk", + "tableFrom": "playground_scripts", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_change_action": { + "name": "schema_check_change_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "schema_change_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "change_message": { + "name": "change_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_breaking": { + "name": "is_breaking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scca_schema_check_id_idx": { + "name": "scca_schema_check_id_idx", + "columns": [ + { + "expression": "schema_check_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_change_action_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_change_action_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_change_action", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_change_operation_usage": { + "name": "schema_check_change_operation_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_change_action_id": { + "name": "schema_check_change_action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_safe_override": { + "name": "is_safe_override", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": { + "sccou_schema_check_change_action_id_idx": { + "name": "sccou_schema_check_change_action_id_idx", + "columns": [ + { + "expression": "schema_check_change_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sccou_federated_graph_id_idx": { + "name": "sccou_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_change_operation_usage_schema_check_change_action_id_schema_check_change_action_id_fk": { + "name": "schema_check_change_operation_usage_schema_check_change_action_id_schema_check_change_action_id_fk", + "tableFrom": "schema_check_change_operation_usage", + "tableTo": "schema_check_change_action", + "columnsFrom": [ + "schema_check_change_action_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schema_check_change_operation_usage_federated_graph_id_federated_graphs_id_fk": { + "name": "schema_check_change_operation_usage_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "schema_check_change_operation_usage", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_composition": { + "name": "schema_check_composition", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "composition_errors": { + "name": "composition_errors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composition_warnings": { + "name": "composition_warnings", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composed_schema_sdl": { + "name": "composed_schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_schema": { + "name": "client_schema", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scc_schema_check_id_idx": { + "name": "scc_schema_check_id_idx", + "columns": [ + { + "expression": "schema_check_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scc_target_id_idx": { + "name": "scc_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_composition_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_composition_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_composition", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schema_check_composition_target_id_targets_id_fk": { + "name": "schema_check_composition_target_id_targets_id_fk", + "tableFrom": "schema_check_composition", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_federated_graphs": { + "name": "schema_check_federated_graphs", + "schema": "", + "columns": { + "check_id": { + "name": "check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "traffic_check_days": { + "name": "traffic_check_days", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "scfg_check_id_idx": { + "name": "scfg_check_id_idx", + "columns": [ + { + "expression": "check_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scfg_federated_graph_id_idx": { + "name": "scfg_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_federated_graphs_check_id_schema_checks_id_fk": { + "name": "schema_check_federated_graphs_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_federated_graphs", + "tableTo": "schema_checks", + "columnsFrom": [ + "check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schema_check_federated_graphs_federated_graph_id_federated_graphs_id_fk": { + "name": "schema_check_federated_graphs_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "schema_check_federated_graphs", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_graph_pruning_action": { + "name": "schema_check_graph_pruning_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "graph_pruning_rule": { + "name": "graph_pruning_rule", + "type": "graph_pruning_rules", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "field_path": { + "name": "field_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_error": { + "name": "is_error", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "location": { + "name": "location", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "scgpa_schema_check_id_idx": { + "name": "scgpa_schema_check_id_idx", + "columns": [ + { + "expression": "schema_check_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scgpa_federated_graph_id_idx": { + "name": "scgpa_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_graph_pruning_action_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_graph_pruning_action_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_graph_pruning_action", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schema_check_graph_pruning_action_federated_graph_id_federated_graphs_id_fk": { + "name": "schema_check_graph_pruning_action_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "schema_check_graph_pruning_action", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_check_lint_action": { + "name": "schema_check_lint_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "lint_rule_type": { + "name": "lint_rule_type", + "type": "lint_rules", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_error": { + "name": "is_error", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "location": { + "name": "location", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sclact_schema_check_id_idx": { + "name": "sclact_schema_check_id_idx", + "columns": [ + { + "expression": "schema_check_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_check_lint_action_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_lint_action_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_lint_action", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_checks": { + "name": "schema_checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_composable": { + "name": "is_composable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "has_breaking_changes": { + "name": "has_breaking_changes", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "has_lint_errors": { + "name": "has_lint_errors", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "has_graph_pruning_errors": { + "name": "has_graph_pruning_errors", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "has_client_traffic": { + "name": "has_client_traffic", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "client_traffic_check_skipped": { + "name": "client_traffic_check_skipped", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "lint_skipped": { + "name": "lint_skipped", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "graph_pruning_skipped": { + "name": "graph_pruning_skipped", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "proposed_subgraph_schema_sdl": { + "name": "proposed_subgraph_schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "gh_details": { + "name": "gh_details", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "forced_success": { + "name": "forced_success", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "vcs_context": { + "name": "vcs_context", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "sc_target_id_idx": { + "name": "sc_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_checks_target_id_targets_id_fk": { + "name": "schema_checks_target_id_targets_id_fk", + "tableFrom": "schema_checks", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_versions": { + "name": "schema_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "schema_sdl": { + "name": "schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_schema": { + "name": "client_schema", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_v2_graph": { + "name": "is_v2_graph", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "sv_organization_id_idx": { + "name": "sv_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sv_target_id_idx": { + "name": "sv_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_versions_organization_id_organizations_id_fk": { + "name": "schema_versions_organization_id_organizations_id_fk", + "tableFrom": "schema_versions", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.schema_version_change_action": { + "name": "schema_version_change_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "schema_change_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "change_message": { + "name": "change_message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "svca_schema_version_id_idx": { + "name": "svca_schema_version_id_idx", + "columns": [ + { + "expression": "schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schema_version_change_action_schema_version_id_schema_versions_id_fk": { + "name": "schema_version_change_action_schema_version_id_schema_versions_id_fk", + "tableFrom": "schema_version_change_action", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_user_id_unique": { + "name": "sessions_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "checkConstraints": {} + }, + "public.slack_installations": { + "name": "slack_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slack_organization_id": { + "name": "slack_organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_organization_name": { + "name": "slack_organization_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_channel_name": { + "name": "slack_channel_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_user_id": { + "name": "slack_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "slack_installations_idx": { + "name": "slack_installations_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slackinst_organization_id_idx": { + "name": "slackinst_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_installations_organization_id_organizations_id_fk": { + "name": "slack_installations_organization_id_organizations_id_fk", + "tableFrom": "slack_installations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.slack_integration_configs": { + "name": "slack_integration_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "slackintconf_integration_id_idx": { + "name": "slackintconf_integration_id_idx", + "columns": [ + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_integration_configs_integration_id_organization_integrations_id_fk": { + "name": "slack_integration_configs_integration_id_organization_integrations_id_fk", + "tableFrom": "slack_integration_configs", + "tableTo": "organization_integrations", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.slack_schema_update_event_configs": { + "name": "slack_schema_update_event_configs", + "schema": "", + "columns": { + "slack_integration_config_id": { + "name": "slack_integration_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "slacksuec_slack_integration_config_id_idx": { + "name": "slacksuec_slack_integration_config_id_idx", + "columns": [ + { + "expression": "slack_integration_config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slacksuec_federated_graph_id_idx": { + "name": "slacksuec_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_schema_update_event_configs_slack_integration_config_id_slack_integration_configs_id_fk": { + "name": "slack_schema_update_event_configs_slack_integration_config_id_slack_integration_configs_id_fk", + "tableFrom": "slack_schema_update_event_configs", + "tableTo": "slack_integration_configs", + "columnsFrom": [ + "slack_integration_config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_schema_update_event_configs_federated_graph_id_federated_graphs_id_fk": { + "name": "slack_schema_update_event_configs_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "slack_schema_update_event_configs", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "slack_schema_update_event_configs_slack_integration_config_id_federated_graph_id_pk": { + "name": "slack_schema_update_event_configs_slack_integration_config_id_federated_graph_id_pk", + "columns": [ + "slack_integration_config_id", + "federated_graph_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.subgraph_members": { + "name": "subgraph_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_id": { + "name": "subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "unique_subgraph_member_idx": { + "name": "unique_subgraph_member_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sm_user_id_idx": { + "name": "sm_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sm_subgraph_id_idx": { + "name": "sm_subgraph_id_idx", + "columns": [ + { + "expression": "subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subgraph_members_user_id_users_id_fk": { + "name": "subgraph_members_user_id_users_id_fk", + "tableFrom": "subgraph_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subgraph_members_subgraph_id_subgraphs_id_fk": { + "name": "subgraph_members_subgraph_id_subgraphs_id_fk", + "tableFrom": "subgraph_members", + "tableTo": "subgraphs", + "columnsFrom": [ + "subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.subgraphs": { + "name": "subgraphs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "routing_url": { + "name": "routing_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_url": { + "name": "subscription_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_protocol": { + "name": "subscription_protocol", + "type": "subscription_protocol", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ws'" + }, + "websocket_subprotocol": { + "name": "websocket_subprotocol", + "type": "websocket_subprotocol", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_feature_subgraph": { + "name": "is_feature_subgraph", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_event_driven_graph": { + "name": "is_event_driven_graph", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "subgraphs_target_id_idx": { + "name": "subgraphs_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subgraphs_schema_version_id_idx": { + "name": "subgraphs_schema_version_id_idx", + "columns": [ + { + "expression": "schema_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subgraphs_schema_version_id_schema_versions_id_fk": { + "name": "subgraphs_schema_version_id_schema_versions_id_fk", + "tableFrom": "subgraphs", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "subgraphs_target_id_targets_id_fk": { + "name": "subgraphs_target_id_targets_id_fk", + "tableFrom": "subgraphs", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.federated_subgraphs": { + "name": "federated_subgraphs", + "schema": "", + "columns": { + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_id": { + "name": "subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "fs_federated_graph_id_idx": { + "name": "fs_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fs_subgraph_id_idx": { + "name": "fs_subgraph_id_idx", + "columns": [ + { + "expression": "subgraph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "federated_subgraphs_federated_graph_id_federated_graphs_id_fk": { + "name": "federated_subgraphs_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "federated_subgraphs", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_subgraphs_subgraph_id_subgraphs_id_fk": { + "name": "federated_subgraphs_subgraph_id_subgraphs_id_fk", + "tableFrom": "federated_subgraphs", + "tableTo": "subgraphs", + "columnsFrom": [ + "subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "federated_subgraphs_federated_graph_id_subgraph_id_pk": { + "name": "federated_subgraphs_federated_graph_id_subgraph_id_pk", + "columns": [ + "federated_graph_id", + "subgraph_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.target_label_matchers": { + "name": "target_label_matchers", + "schema": "", + "columns": { + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label_matcher": { + "name": "label_matcher", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tlm_target_id_idx": { + "name": "tlm_target_id_idx", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "target_label_matchers_target_id_targets_id_fk": { + "name": "target_label_matchers_target_id_targets_id_fk", + "tableFrom": "target_label_matchers", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.targets": { + "name": "targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "target_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "labels": { + "name": "labels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "readme": { + "name": "readme", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "namespace_id": { + "name": "namespace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "organization_name_idx": { + "name": "organization_name_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "targets_organization_id_idx": { + "name": "targets_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "targets_created_by_idx": { + "name": "targets_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "targets_namespace_id_idx": { + "name": "targets_namespace_id_idx", + "columns": [ + { + "expression": "namespace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "targets_organization_id_organizations_id_fk": { + "name": "targets_organization_id_organizations_id_fk", + "tableFrom": "targets", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "targets_created_by_users_id_fk": { + "name": "targets_created_by_users_id_fk", + "tableFrom": "targets", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "targets_namespace_id_namespaces_id_fk": { + "name": "targets_namespace_id_namespaces_id_fk", + "tableFrom": "targets", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "checkConstraints": {} + }, + "public.webhook_deliveries": { + "name": "webhook_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "webhook_delivery_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_name": { + "name": "event_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_headers": { + "name": "request_headers", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "response_headers": { + "name": "response_headers", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_error_code": { + "name": "response_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "duration": { + "name": "duration", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "original_delivery_id": { + "name": "original_delivery_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "webhd_organization_id_idx": { + "name": "webhd_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhd_created_by_id_idx": { + "name": "webhd_created_by_id_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_deliveries_created_by_id_users_id_fk": { + "name": "webhook_deliveries_created_by_id_users_id_fk", + "tableFrom": "webhook_deliveries", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "webhook_deliveries_organization_id_organizations_id_fk": { + "name": "webhook_deliveries_organization_id_organizations_id_fk", + "tableFrom": "webhook_deliveries", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public.webhook_graph_schema_update": { + "name": "webhook_graph_schema_update", + "schema": "", + "columns": { + "webhook_id": { + "name": "webhook_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "wgsu_webhook_id_idx": { + "name": "wgsu_webhook_id_idx", + "columns": [ + { + "expression": "webhook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "wgsu_federated_graph_id_idx": { + "name": "wgsu_federated_graph_id_idx", + "columns": [ + { + "expression": "federated_graph_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_graph_schema_update_webhook_id_organization_webhook_configs_id_fk": { + "name": "webhook_graph_schema_update_webhook_id_organization_webhook_configs_id_fk", + "tableFrom": "webhook_graph_schema_update", + "tableTo": "organization_webhook_configs", + "columnsFrom": [ + "webhook_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_graph_schema_update_federated_graph_id_federated_graphs_id_fk": { + "name": "webhook_graph_schema_update_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "webhook_graph_schema_update", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "webhook_graph_schema_update_webhook_id_federated_graph_id_pk": { + "name": "webhook_graph_schema_update_webhook_id_federated_graph_id_pk", + "columns": [ + "webhook_id", + "federated_graph_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "enums": { + "public.git_installation_type": { + "name": "git_installation_type", + "schema": "public", + "values": [ + "PERSONAL", + "ORGANIZATION" + ] + }, + "public.graph_composition_subgraph_change_type": { + "name": "graph_composition_subgraph_change_type", + "schema": "public", + "values": [ + "added", + "removed", + "updated", + "unchanged" + ] + }, + "public.graph_pruning_rules": { + "name": "graph_pruning_rules", + "schema": "public", + "values": [ + "UNUSED_FIELDS", + "DEPRECATED_FIELDS", + "REQUIRE_DEPRECATION_BEFORE_DELETION" + ] + }, + "public.integration_type": { + "name": "integration_type", + "schema": "public", + "values": [ + "slack" + ] + }, + "public.lint_rules": { + "name": "lint_rules", + "schema": "public", + "values": [ + "FIELD_NAMES_SHOULD_BE_CAMEL_CASE", + "TYPE_NAMES_SHOULD_BE_PASCAL_CASE", + "SHOULD_NOT_HAVE_TYPE_PREFIX", + "SHOULD_NOT_HAVE_TYPE_SUFFIX", + "SHOULD_NOT_HAVE_INPUT_PREFIX", + "SHOULD_HAVE_INPUT_SUFFIX", + "SHOULD_NOT_HAVE_ENUM_PREFIX", + "SHOULD_NOT_HAVE_ENUM_SUFFIX", + "SHOULD_NOT_HAVE_INTERFACE_PREFIX", + "SHOULD_NOT_HAVE_INTERFACE_SUFFIX", + "ENUM_VALUES_SHOULD_BE_UPPER_CASE", + "ORDER_FIELDS", + "ORDER_ENUM_VALUES", + "ORDER_DEFINITIONS", + "ALL_TYPES_REQUIRE_DESCRIPTION", + "DISALLOW_CASE_INSENSITIVE_ENUM_VALUES", + "NO_TYPENAME_PREFIX_IN_TYPE_FIELDS", + "REQUIRE_DEPRECATION_REASON" + ] + }, + "public.lint_severity": { + "name": "lint_severity", + "schema": "public", + "values": [ + "warn", + "error" + ] + }, + "public.member_role": { + "name": "member_role", + "schema": "public", + "values": [ + "admin", + "developer", + "viewer" + ] + }, + "public.playground_script_type": { + "name": "playground_script_type", + "schema": "public", + "values": [ + "pre-flight", + "pre-operation", + "post-operation" + ] + }, + "public.schema_change_type": { + "name": "schema_change_type", + "schema": "public", + "values": [ + "FIELD_ARGUMENT_DESCRIPTION_CHANGED", + "FIELD_ARGUMENT_DEFAULT_CHANGED", + "FIELD_ARGUMENT_TYPE_CHANGED", + "DIRECTIVE_REMOVED", + "DIRECTIVE_ADDED", + "DIRECTIVE_DESCRIPTION_CHANGED", + "DIRECTIVE_LOCATION_ADDED", + "DIRECTIVE_LOCATION_REMOVED", + "DIRECTIVE_ARGUMENT_ADDED", + "DIRECTIVE_ARGUMENT_REMOVED", + "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED", + "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED", + "DIRECTIVE_ARGUMENT_TYPE_CHANGED", + "ENUM_VALUE_REMOVED", + "ENUM_VALUE_ADDED", + "ENUM_VALUE_DESCRIPTION_CHANGED", + "ENUM_VALUE_DEPRECATION_REASON_CHANGED", + "ENUM_VALUE_DEPRECATION_REASON_ADDED", + "ENUM_VALUE_DEPRECATION_REASON_REMOVED", + "FIELD_REMOVED", + "FIELD_ADDED", + "FIELD_DESCRIPTION_CHANGED", + "FIELD_DESCRIPTION_ADDED", + "FIELD_DESCRIPTION_REMOVED", + "FIELD_DEPRECATION_ADDED", + "FIELD_DEPRECATION_REMOVED", + "FIELD_DEPRECATION_REASON_CHANGED", + "FIELD_DEPRECATION_REASON_ADDED", + "FIELD_DEPRECATION_REASON_REMOVED", + "FIELD_TYPE_CHANGED", + "FIELD_ARGUMENT_ADDED", + "FIELD_ARGUMENT_REMOVED", + "INPUT_FIELD_REMOVED", + "INPUT_FIELD_ADDED", + "INPUT_FIELD_DESCRIPTION_ADDED", + "INPUT_FIELD_DESCRIPTION_REMOVED", + "INPUT_FIELD_DESCRIPTION_CHANGED", + "INPUT_FIELD_DEFAULT_VALUE_CHANGED", + "INPUT_FIELD_TYPE_CHANGED", + "OBJECT_TYPE_INTERFACE_ADDED", + "OBJECT_TYPE_INTERFACE_REMOVED", + "SCHEMA_QUERY_TYPE_CHANGED", + "SCHEMA_MUTATION_TYPE_CHANGED", + "SCHEMA_SUBSCRIPTION_TYPE_CHANGED", + "TYPE_REMOVED", + "TYPE_ADDED", + "TYPE_KIND_CHANGED", + "TYPE_DESCRIPTION_CHANGED", + "TYPE_DESCRIPTION_REMOVED", + "TYPE_DESCRIPTION_ADDED", + "UNION_MEMBER_REMOVED", + "UNION_MEMBER_ADDED" + ] + }, + "public.subscription_protocol": { + "name": "subscription_protocol", + "schema": "public", + "values": [ + "ws", + "sse", + "sse_post" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "incomplete", + "incomplete_expired", + "trialing", + "active", + "past_due", + "canceled", + "unpaid", + "paused" + ] + }, + "public.target_type": { + "name": "target_type", + "schema": "public", + "values": [ + "federated", + "subgraph" + ] + }, + "public.webhook_delivery_type": { + "name": "webhook_delivery_type", + "schema": "public", + "values": [ + "webhook", + "slack", + "admission" + ] + }, + "public.websocket_subprotocol": { + "name": "websocket_subprotocol", + "schema": "public", + "values": [ + "auto", + "graphql-ws", + "graphql-transport-ws" + ] + } + }, + "schemas": {}, + "sequences": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/controlplane/migrations/meta/_journal.json b/controlplane/migrations/meta/_journal.json index 23a2d729d2..9bb47e33aa 100644 --- a/controlplane/migrations/meta/_journal.json +++ b/controlplane/migrations/meta/_journal.json @@ -757,6 +757,13 @@ "when": 1730206373359, "tag": "0107_tearful_phalanx", "breakpoints": true + }, + { + "idx": 108, + "version": "7", + "when": 1730821051787, + "tag": "0108_dazzling_iron_lad", + "breakpoints": true } ] } \ No newline at end of file diff --git a/controlplane/src/bin/deactivate-org.ts b/controlplane/src/bin/deactivate-org.ts index d3960242bc..229ac3d1df 100644 --- a/controlplane/src/bin/deactivate-org.ts +++ b/controlplane/src/bin/deactivate-org.ts @@ -1,45 +1,14 @@ import process from 'node:process'; -import { drizzle } from 'drizzle-orm/postgres-js'; import { pino } from 'pino'; -import postgres from 'postgres'; -import { buildDatabaseConnectionConfig } from '../core/plugins/database.js'; -import { OrganizationRepository } from '../core/repositories/OrganizationRepository.js'; -import * as schema from '../db/schema.js'; -import Keycloak from '../core/services/Keycloak.js'; -import { createDeleteOrganizationWorker, DeleteOrganizationQueue } from '../core/workers/DeleteOrganizationWorker.js'; +import { DeactivateOrganizationQueue } from '../core/workers/DeactivateOrganizationWorker.js'; import { createRedisConnections } from '../core/plugins/redis.js'; import { getConfig } from './get-config.js'; -const { - realm, - loginRealm, - adminUser, - adminPassword, - clientId, - apiUrl, - databaseConnectionUrl, - databaseTlsCa, - databaseTlsCert, - databaseTlsKey, - organizationSlug, - redis, -} = getConfig(); +const { organizationSlug, redis } = getConfig(); + const organizationId = process.env.ORGANIZATION_ID || ''; const deactivationReason = process.env.ORGANIZATION_DEACTIVATION_REASON; -// Establish database connection -const connectionConfig = await buildDatabaseConnectionConfig({ - tls: - databaseTlsCa || databaseTlsCert || databaseTlsKey - ? { ca: databaseTlsCa, cert: databaseTlsCert, key: databaseTlsKey } - : undefined, -}); -const queryConnection = postgres(databaseConnectionUrl, { - ...connectionConfig, - max: 1, -}); -const db = drizzle(queryConnection, { schema: { ...schema } }); - const { redisQueue, redisWorker } = await createRedisConnections({ host: redis.host!, port: Number(redis.port), @@ -52,52 +21,15 @@ await redisWorker.connect(); await redisWorker.ping(); await redisQueue.ping(); -const keycloakClient = new Keycloak({ - apiUrl, - realm: loginRealm, - clientId, - adminUser, - adminPassword, -}); -await keycloakClient.authenticateClient(); - const logger = pino(); -const orgRepo = new OrganizationRepository(logger, db); - -const org = await orgRepo.bySlug(organizationSlug); -if (!org) { - throw new Error('Organization not found'); -} +const deactivateOrganizationQueue = new DeactivateOrganizationQueue(logger, redisQueue); -if (org.id !== organizationId) { - throw new Error('Id and slug mismatch'); -} - -const deleteOrganizationQueue = new DeleteOrganizationQueue(logger, redisQueue); - -const worker = createDeleteOrganizationWorker({ - redisConnection: redisWorker, - db, - logger, - keycloakClient, - keycloakRealm: realm, -}); - -await orgRepo.deactivateOrganization({ +await deactivateOrganizationQueue.addJob({ organizationId, - reason: deactivationReason, - keycloakClient, - keycloakRealm: realm, - deleteOrganizationQueue, + organizationSlug, + deactivationReason, }); -await worker.close(); - redisQueue.disconnect(); redisWorker.disconnect(); - -// Close database connection -await queryConnection.end({ - timeout: 1, -}); diff --git a/controlplane/src/bin/delete-user.ts b/controlplane/src/bin/delete-user.ts index 631fc760d9..292a51772e 100644 --- a/controlplane/src/bin/delete-user.ts +++ b/controlplane/src/bin/delete-user.ts @@ -1,97 +1,34 @@ import process from 'node:process'; -import { drizzle } from 'drizzle-orm/postgres-js'; import { pino } from 'pino'; -import postgres from 'postgres'; -import { PlatformEventName } from '@wundergraph/cosmo-connect/dist/notifications/events_pb'; -import { buildDatabaseConnectionConfig } from '../core/plugins/database.js'; -import { UserRepository } from '../core/repositories/UserRepository.js'; -import { PlatformWebhookService } from '../core/webhooks/PlatformWebhookService.js'; -import Keycloak from '../core/services/Keycloak.js'; -import * as schema from '../db/schema.js'; -import { OrganizationRepository } from '../core/repositories/OrganizationRepository.js'; +import { createRedisConnections } from '../core/plugins/redis.js'; +import { DeleteUserQueue } from '../core/workers/DeleteUserQueue.js'; import { getConfig } from './get-config.js'; -const { - realm, - loginRealm, - adminUser, - adminPassword, - clientId, - apiUrl, - databaseConnectionUrl, - databaseTlsCa, - databaseTlsCert, - databaseTlsKey, - webhookUrl, - webhookSecret, -} = getConfig(); +const { redis } = getConfig(); const userId = process.env.USER_ID || ''; +const userEmail = process.env.USER_EMAIL || ''; -// Establish database connection -const connectionConfig = await buildDatabaseConnectionConfig({ - tls: - databaseTlsCa || databaseTlsCert || databaseTlsKey - ? { ca: databaseTlsCa, cert: databaseTlsCert, key: databaseTlsKey } - : undefined, +const { redisQueue, redisWorker } = await createRedisConnections({ + host: redis.host!, + port: Number(redis.port), + password: redis.password, + tls: redis.tls, }); -const queryConnection = postgres(databaseConnectionUrl, { - ...connectionConfig, - max: 1, -}); -const db = drizzle(queryConnection, { schema: { ...schema } }); -// Authenticate with keycloak -const keycloakClient = new Keycloak({ - apiUrl, - realm: loginRealm, - clientId, - adminUser, - adminPassword, -}); -await keycloakClient.authenticateClient(); +await redisQueue.connect(); +await redisWorker.connect(); +await redisWorker.ping(); +await redisQueue.ping(); const logger = pino(); -// Init platform webhooks -const platformWebhooks = new PlatformWebhookService(webhookUrl, webhookSecret, logger); - -// Find user on keycloak -const user = await keycloakClient.client.users.findOne({ - realm, - id: userId, -}); - -const userRepo = new UserRepository(logger, db); -const orgRepo = new OrganizationRepository(logger, db); - -if (!user || !user.id || !user.email) { - throw new Error('User not found'); -} +const deleteUserQueue = new DeleteUserQueue(logger, redisQueue); -// Check if user can be deleted -const { isSafe, soloOrganizations, unsafeOrganizations } = await orgRepo.canUserBeDeleted(user.id); - -console.log(`soloOrganizations=${JSON.stringify(soloOrganizations)}\n`); -console.log(`unsafeOrganizations=${JSON.stringify(unsafeOrganizations)}\n`); - -if (!isSafe) { - throw new Error('Cannot delete user because they are the only admin of an organization with several members.'); -} - -// Delete the user -await userRepo.deleteUser({ - id: user.id, - keycloakClient, - keycloakRealm: realm, +await deleteUserQueue.addJob({ + userId, + userEmail, }); -platformWebhooks.send(PlatformEventName.USER_DELETE_SUCCESS, { - user_id: user.id, - user_email: user.email!, -}); - -// Close database connection -await queryConnection.end({ - timeout: 1, -}); +redisQueue.disconnect(); +redisWorker.disconnect(); diff --git a/controlplane/src/bin/get-config.ts b/controlplane/src/bin/get-config.ts index 2305b7e809..0a78771137 100644 --- a/controlplane/src/bin/get-config.ts +++ b/controlplane/src/bin/get-config.ts @@ -40,6 +40,15 @@ const getConfig = () => { webhookUrl: process.env.WEBHOOK_URL, webhookSecret: process.env.WEBHOOK_SECRET, + + s3Storage: { + url: process.env.S3_STORAGE_URL || 'http://minio:changeme@localhost:10000/cosmo', + endpoint: process.env.S3_ENDPOINT, + region: process.env.S3_REGION || 'auto', + username: process.env.S3_ACCESS_KEY_ID, + password: process.env.S3_SECRET_ACCESS_KEY, + forcePathStyle: process.env.S3_FORCE_PATH_STYLE === undefined ? true : process.env.S3_FORCE_PATH_STYLE === 'true', + }, }; }; diff --git a/controlplane/src/bin/reactivate-org.ts b/controlplane/src/bin/reactivate-org.ts index c6ea9c32ef..787354fc31 100644 --- a/controlplane/src/bin/reactivate-org.ts +++ b/controlplane/src/bin/reactivate-org.ts @@ -1,44 +1,12 @@ import process from 'node:process'; -import { drizzle } from 'drizzle-orm/postgres-js'; import { pino } from 'pino'; -import postgres from 'postgres'; -import { buildDatabaseConnectionConfig } from '../core/plugins/database.js'; -import { OrganizationRepository } from '../core/repositories/OrganizationRepository.js'; -import * as schema from '../db/schema.js'; -import Keycloak from '../core/services/Keycloak.js'; -import { createDeleteOrganizationWorker, DeleteOrganizationQueue } from '../core/workers/DeleteOrganizationWorker.js'; import { createRedisConnections } from '../core/plugins/redis.js'; +import { ReactivateOrganizationQueue } from '../core/workers/ReactivateOrganizationWorker.js'; import { getConfig } from './get-config.js'; -const { - realm, - loginRealm, - adminUser, - adminPassword, - clientId, - apiUrl, - databaseConnectionUrl, - databaseTlsCa, - databaseTlsCert, - databaseTlsKey, - organizationSlug, - redis, -} = getConfig(); -const organizationId = process.env.ORGANIZATION_ID || ''; -const deactivationReason = process.env.ORGANIZATION_DEACTIVATION_REASON; +const { organizationSlug, redis } = getConfig(); -// Establish database connection -const connectionConfig = await buildDatabaseConnectionConfig({ - tls: - databaseTlsCa || databaseTlsCert || databaseTlsKey - ? { ca: databaseTlsCa, cert: databaseTlsCert, key: databaseTlsKey } - : undefined, -}); -const queryConnection = postgres(databaseConnectionUrl, { - ...connectionConfig, - max: 1, -}); -const db = drizzle(queryConnection, { schema: { ...schema } }); +const organizationId = process.env.ORGANIZATION_ID || ''; const { redisQueue, redisWorker } = await createRedisConnections({ host: redis.host!, @@ -52,49 +20,14 @@ await redisWorker.connect(); await redisWorker.ping(); await redisQueue.ping(); -const keycloakClient = new Keycloak({ - apiUrl, - realm: loginRealm, - clientId, - adminUser, - adminPassword, -}); -await keycloakClient.authenticateClient(); - const logger = pino(); -const orgRepo = new OrganizationRepository(logger, db); - -const org = await orgRepo.bySlug(organizationSlug); -if (!org) { - throw new Error('Organization not found'); -} +const reactivateOrganizationQueue = new ReactivateOrganizationQueue(logger, redisQueue); -if (org.id !== organizationId) { - throw new Error('Id and slug mismatch'); -} - -const deleteOrganizationQueue = new DeleteOrganizationQueue(logger, redisQueue); - -const worker = createDeleteOrganizationWorker({ - redisConnection: redisWorker, - db, - logger, - keycloakClient, - keycloakRealm: realm, -}); - -await orgRepo.reactivateOrganization({ +await reactivateOrganizationQueue.addJob({ organizationId, - deleteOrganizationQueue, + organizationSlug, }); -await worker.close(); - redisQueue.disconnect(); redisWorker.disconnect(); - -// Close database connection -await queryConnection.end({ - timeout: 1, -}); diff --git a/controlplane/src/core/bufservices/organization/deleteOrganization.ts b/controlplane/src/core/bufservices/organization/deleteOrganization.ts index 5830cce28a..1c68ca783e 100644 --- a/controlplane/src/core/bufservices/organization/deleteOrganization.ts +++ b/controlplane/src/core/bufservices/organization/deleteOrganization.ts @@ -89,7 +89,7 @@ export function deleteOrganization( }); } - await orgRepo.deleteOrganization(authContext.organizationId); + await orgRepo.deleteOrganization(authContext.organizationId, opts.blobStorage); await opts.keycloakClient.deleteOrganizationGroup({ realm: opts.keycloakRealm, diff --git a/controlplane/src/core/bufservices/user/deleteUser.ts b/controlplane/src/core/bufservices/user/deleteUser.ts index 8573f61b3b..09f0abe791 100644 --- a/controlplane/src/core/bufservices/user/deleteUser.ts +++ b/controlplane/src/core/bufservices/user/deleteUser.ts @@ -40,11 +40,14 @@ export function deleteUser( await opts.keycloakClient.authenticateClient(); // Delete the user - await userRepo.deleteUser({ - id: authContext.userId, - keycloakClient: opts.keycloakClient, - keycloakRealm: opts.keycloakRealm, - }); + await userRepo.deleteUser( + { + id: authContext.userId, + keycloakClient: opts.keycloakClient, + keycloakRealm: opts.keycloakRealm, + }, + opts.blobStorage, + ); opts.platformWebhooks.send(PlatformEventName.USER_DELETE_SUCCESS, { user_id: authContext.userId, diff --git a/controlplane/src/core/bufservices/user/removeInvitation.ts b/controlplane/src/core/bufservices/user/removeInvitation.ts index 314e02b1ef..ad0f04e6c3 100644 --- a/controlplane/src/core/bufservices/user/removeInvitation.ts +++ b/controlplane/src/core/bufservices/user/removeInvitation.ts @@ -100,11 +100,14 @@ export function removeInvitation( // this will happen only when the user was invited but the user didn't login and the admin removed that user, // in this case the user will not have a personal org if (userMemberships.length === 0 && userPendingInvitations.length === 0) { - await userRepo.deleteUser({ - id: user.id, - keycloakClient: opts.keycloakClient, - keycloakRealm: opts.keycloakRealm, - }); + await userRepo.deleteUser( + { + id: user.id, + keycloakClient: opts.keycloakClient, + keycloakRealm: opts.keycloakRealm, + }, + opts.blobStorage, + ); } await auditLogRepo.addAuditLog({ diff --git a/controlplane/src/core/bufservices/user/removeOrganizationMember.ts b/controlplane/src/core/bufservices/user/removeOrganizationMember.ts index 153ae37f94..582dbd61d4 100644 --- a/controlplane/src/core/bufservices/user/removeOrganizationMember.ts +++ b/controlplane/src/core/bufservices/user/removeOrganizationMember.ts @@ -111,11 +111,14 @@ export function removeOrganizationMember( // this will happen only when the user was invited but the user didn't login and the admin removed that user, // in this case the user will not have a personal org if (userMemberships.length === 0) { - await userRepo.deleteUser({ - id: user.id, - keycloakClient: opts.keycloakClient, - keycloakRealm: opts.keycloakRealm, - }); + await userRepo.deleteUser( + { + id: user.id, + keycloakClient: opts.keycloakClient, + keycloakRealm: opts.keycloakRealm, + }, + opts.blobStorage, + ); } await auditLogRepo.addAuditLog({ diff --git a/controlplane/src/core/build-server.ts b/controlplane/src/core/build-server.ts index 70920a5f39..a2370720a6 100644 --- a/controlplane/src/core/build-server.ts +++ b/controlplane/src/core/build-server.ts @@ -40,6 +40,15 @@ import { AIGraphReadmeQueue, createAIGraphReadmeWorker } from './workers/AIGraph import { fastifyLoggerId, createS3ClientConfig, extractS3BucketName } from './util.js'; import { ApiKeyRepository } from './repositories/ApiKeyRepository.js'; import { createDeleteOrganizationWorker, DeleteOrganizationQueue } from './workers/DeleteOrganizationWorker.js'; +import { + createDeactivateOrganizationWorker, + DeactivateOrganizationQueue, +} from './workers/DeactivateOrganizationWorker.js'; +import { createDeleteUserWorker, DeleteUserQueue } from './workers/DeleteUserQueue.js'; +import { + createReactivateOrganizationWorker, + ReactivateOrganizationQueue, +} from './workers/ReactivateOrganizationWorker.js'; export interface BuildConfig { logger: LoggerOptions; @@ -287,6 +296,18 @@ export default async function build(opts: BuildConfig) { tls: opts.redis.tls, }); + if (!opts.s3Storage || !opts.s3Storage.url) { + throw new Error('S3 storage URL is required'); + } + + const bucketName = extractS3BucketName(opts.s3Storage); + const s3Config = createS3ClientConfig(bucketName, opts.s3Storage); + + const s3Client = new S3Client(s3Config); + const blobStorage = new S3BlobStorage(s3Client, bucketName); + + const platformWebhooks = new PlatformWebhookService(opts.webhook?.url, opts.webhook?.key, logger); + const readmeQueue = new AIGraphReadmeQueue(logger, fastify.redisForQueue); if (opts.openaiAPIKey) { @@ -308,6 +329,42 @@ export default async function build(opts: BuildConfig) { logger, keycloakClient, keycloakRealm: opts.keycloak.realm, + blobStorage, + }), + ); + + const deactivateOrganizationQueue = new DeactivateOrganizationQueue(logger, fastify.redisForQueue); + bullWorkers.push( + createDeactivateOrganizationWorker({ + redisConnection: fastify.redisForWorker, + db: fastify.db, + logger, + keycloakClient, + keycloakRealm: opts.keycloak.realm, + deleteOrganizationQueue, + }), + ); + + const reactivateOrganizationQueue = new ReactivateOrganizationQueue(logger, fastify.redisForQueue); + bullWorkers.push( + createReactivateOrganizationWorker({ + redisConnection: fastify.redisForWorker, + db: fastify.db, + logger, + deleteOrganizationQueue, + }), + ); + + const deleteUserQueue = new DeleteUserQueue(logger, fastify.redisForQueue); + bullWorkers.push( + createDeleteUserWorker({ + redisConnection: fastify.redisForWorker, + db: fastify.db, + logger, + keycloakClient, + keycloakRealm: opts.keycloak.realm, + blobStorage, + platformWebhooks, }), ); @@ -350,22 +407,10 @@ export default async function build(opts: BuildConfig) { }); } - if (!opts.s3Storage || !opts.s3Storage.url) { - throw new Error('S3 storage URL is required'); - } - - const bucketName = extractS3BucketName(opts.s3Storage); - const s3Config = createS3ClientConfig(bucketName, opts.s3Storage); - - const s3Client = new S3Client(s3Config); - const blobStorage = new S3BlobStorage(s3Client, bucketName); - /** * Controllers registration */ - const platformWebhooks = new PlatformWebhookService(opts.webhook?.url, opts.webhook?.key, logger); - await fastify.register(AuthController, { organizationRepository, orgInvitationRepository, @@ -423,6 +468,9 @@ export default async function build(opts: BuildConfig) { queues: { readmeQueue, deleteOrganizationQueue, + deactivateOrganizationQueue, + reactivateOrganizationQueue, + deleteUserQueue, }, stripeSecretKey: opts.stripe?.secret, admissionWebhookJWTSecret: opts.admissionWebhook.secret, diff --git a/controlplane/src/core/repositories/OrganizationRepository.ts b/controlplane/src/core/repositories/OrganizationRepository.ts index 1acfbfbe5f..3bd821b390 100644 --- a/controlplane/src/core/repositories/OrganizationRepository.ts +++ b/controlplane/src/core/repositories/OrganizationRepository.ts @@ -29,9 +29,12 @@ import { import { Feature, FeatureIds, OrganizationDTO, OrganizationMemberDTO, WebhooksConfigDTO } from '../../types/index.js'; import Keycloak from '../services/Keycloak.js'; import { DeleteOrganizationQueue } from '../workers/DeleteOrganizationWorker.js'; +import { BlobStorage } from '../blobstorage/index.js'; import { BillingRepository } from './BillingRepository.js'; import { FederatedGraphRepository } from './FederatedGraphRepository.js'; import { OidcRepository } from './OidcRepository.js'; +import { SubgraphRepository } from './SubgraphRepository.js'; +import { TargetRepository } from './TargetRepository.js'; /** * Repository for organization related operations. @@ -849,13 +852,32 @@ export class OrganizationRepository { return result[0]; } - public deleteOrganization(organizationId: string) { + /** + This manually deletes graphs from db and blob storage. + Everything else is deleted automatically by db constraints + */ + public deleteOrganization(organizationId: string, blobStorage: BlobStorage) { return this.db.transaction(async (tx) => { - const oidcRepo = new OidcRepository(tx); - await oidcRepo.deleteOidcProvider({ organizationId }); + const fedGraphRepo = new FederatedGraphRepository(this.logger, tx, organizationId); + const targetRepo = new TargetRepository(tx, organizationId); + + const graphs = await fedGraphRepo.list({ + limit: 0, + offset: 0, + }); + + await targetRepo.deleteAll(); + + // Clean up blob storage + const blobPromises = []; + for (const graph of graphs) { + const blobStorageDirectory = `${organizationId}/${graph.id}`; + blobPromises.push(blobStorage.removeDirectory({ key: blobStorageDirectory })); + } + await Promise.allSettled(blobPromises); // Delete organization from db - await this.db.delete(organizations).where(eq(organizations.id, organizationId)).execute(); + await tx.delete(organizations).where(eq(organizations.id, organizationId)).execute(); }); } diff --git a/controlplane/src/core/repositories/TargetRepository.ts b/controlplane/src/core/repositories/TargetRepository.ts index 3a008dbce8..8840f22854 100644 --- a/controlplane/src/core/repositories/TargetRepository.ts +++ b/controlplane/src/core/repositories/TargetRepository.ts @@ -58,4 +58,8 @@ export class TargetRepository { await this.db.update(targets).set({ namespaceId: newNamespaceId }).where(inArray(targets.id, targetIds)); } + + public deleteAll() { + return this.db.delete(targets).where(eq(targets.organizationId, this.organizationId)).execute(); + } } diff --git a/controlplane/src/core/repositories/UserRepository.ts b/controlplane/src/core/repositories/UserRepository.ts index f9c617e28a..0511ba4641 100644 --- a/controlplane/src/core/repositories/UserRepository.ts +++ b/controlplane/src/core/repositories/UserRepository.ts @@ -4,11 +4,12 @@ import { FastifyBaseLogger } from 'fastify'; import * as schema from '../../db/schema.js'; import { users } from '../../db/schema.js'; import { UserDTO } from '../../types/index.js'; +import { BlobStorage } from '../blobstorage/index.js'; import Keycloak from '../services/Keycloak.js'; import OidcProvider from '../services/OidcProvider.js'; -import { OrganizationRepository } from './OrganizationRepository.js'; import { BillingRepository } from './BillingRepository.js'; import { OidcRepository } from './OidcRepository.js'; +import { OrganizationRepository } from './OrganizationRepository.js'; /** * Repository for user related operations. @@ -65,75 +66,108 @@ export class UserRepository { .execute(); } - public async deleteUser(input: { id: string; keycloakClient: Keycloak; keycloakRealm: string }) { + public async deleteUser( + input: { id: string; keycloakClient: Keycloak; keycloakRealm: string }, + blobStorage: BlobStorage, + ) { const orgRepo = new OrganizationRepository(this.logger, this.db); - const billingRepo = new BillingRepository(this.db); - - const { soloAdminSoloMemberOrgs, memberships } = await orgRepo.adminMemberships({ userId: input.id }); + const oidcRepo = new OidcRepository(this.db); - // Cancel subscriptions and remove oidc providers - for (const org of soloAdminSoloMemberOrgs) { - await billingRepo.cancelSubscription(org.id); - - const oidcRepo = new OidcRepository(this.db); - const oidcProvider = new OidcProvider(); + // get all memberships + const orgMemberships = await orgRepo.adminMemberships({ userId: input.id }); + // get all providers + const oidcProviders = []; + for (const org of orgMemberships.soloAdminSoloMemberOrgs) { const provider = await oidcRepo.getOidcProvider({ organizationId: org.id }); if (provider) { - await oidcProvider.deleteOidcProvider({ - kcClient: input.keycloakClient, - kcRealm: input.keycloakRealm, - organizationSlug: org.slug, - alias: provider.alias, - }); + oidcProviders.push({ ...provider, orgSlug: org.slug }); } } - // Remove keycloak user from all org groups - for (const org of memberships) { - const orgMember = await orgRepo.getOrganizationMember({ - organizationID: org.id, - userID: input.id, - }); - - if (!orgMember) { - throw new Error('Organization member not found'); - } - - await input.keycloakClient.removeUserFromOrganization({ - realm: input.keycloakRealm, - userID: input.id, - groupName: org.slug, - roles: orgMember.roles, - }); - } + // Perform Keycloak deletions. + await this.deleteUserFromKeycloak({ ...input, oidcProviders, orgMemberships }); + // Perform DB deletions await this.db.transaction(async (tx) => { const orgRepo = new OrganizationRepository(this.logger, tx); + const billingRepo = new BillingRepository(tx); + + // Cancel subscriptions and remove oidc providers + for (const org of orgMemberships.soloAdminSoloMemberOrgs) { + await billingRepo.cancelSubscription(org.id); + } // Delete all solo organizations of the user const deleteOrgs: Promise[] = []; - for (const org of soloAdminSoloMemberOrgs) { - deleteOrgs.push(orgRepo.deleteOrganization(org.id)); + for (const org of orgMemberships.soloAdminSoloMemberOrgs) { + deleteOrgs.push(orgRepo.deleteOrganization(org.id, blobStorage)); } await Promise.all(deleteOrgs); // Delete from db await tx.delete(users).where(eq(users.id, input.id)).execute(); }); + } + + private async deleteUserFromKeycloak(input: { + id: string; + oidcProviders: { alias: string; orgSlug: string }[]; + orgMemberships: { + memberships: { slug: string; roles: string[] }[]; + soloAdminSoloMemberOrgs: { slug: string }[]; + }; + keycloakClient: Keycloak; + keycloakRealm: string; + }) { + try { + const oidcProvider = new OidcProvider(); + + // Remove OIDC providers + for (const provider of input.oidcProviders) { + await oidcProvider.deleteOidcProvider({ + kcClient: input.keycloakClient, + kcRealm: input.keycloakRealm, + organizationSlug: provider.orgSlug, + alias: provider.alias, + }); + } - for (const org of soloAdminSoloMemberOrgs) { - await input.keycloakClient.deleteOrganizationGroup({ + // Remove keycloak user from all org groups + for (const org of input.orgMemberships.memberships) { + await input.keycloakClient.removeUserFromOrganization({ + realm: input.keycloakRealm, + userID: input.id, + groupName: org.slug, + roles: org.roles, + }); + } + + // Delete keycloak organization groups + for (const org of input.orgMemberships.soloAdminSoloMemberOrgs) { + await input.keycloakClient.deleteOrganizationGroup({ + realm: input.keycloakRealm, + organizationSlug: org.slug, + }); + } + + // Delete user from keycloak + await input.keycloakClient.client.users.del({ + id: input.id, realm: input.keycloakRealm, - organizationSlug: org.slug, }); + } catch (e: any) { + this.logger.error( + { + userId: input.id, + error: e.message, + oidcProviders: input.oidcProviders, + ...input.orgMemberships, + }, + 'Error deleting user details from keycloak.', + ); + throw e; } - - // Delete user from keycloak - await input.keycloakClient.client.users.del({ - id: input.id, - realm: input.keycloakRealm, - }); } // only to update the active attribute diff --git a/controlplane/src/core/routes.ts b/controlplane/src/core/routes.ts index f399e2e06c..ebb4b9d813 100644 --- a/controlplane/src/core/routes.ts +++ b/controlplane/src/core/routes.ts @@ -17,6 +17,9 @@ import Mailer from './services/Mailer.js'; import { Authorization } from './services/Authorization.js'; import { AIGraphReadmeQueue } from './workers/AIGraphReadmeWorker.js'; import { DeleteOrganizationQueue } from './workers/DeleteOrganizationWorker.js'; +import { DeactivateOrganizationQueue } from './workers/DeactivateOrganizationWorker.js'; +import { DeleteUserQueue } from './workers/DeleteUserQueue.js'; +import { ReactivateOrganizationQueue } from './workers/ReactivateOrganizationWorker.js'; export interface RouterOptions { db: PostgresJsDatabase; @@ -40,6 +43,9 @@ export interface RouterOptions { queues: { readmeQueue: AIGraphReadmeQueue; deleteOrganizationQueue: DeleteOrganizationQueue; + deactivateOrganizationQueue: DeactivateOrganizationQueue; + reactivateOrganizationQueue: ReactivateOrganizationQueue; + deleteUserQueue: DeleteUserQueue; }; stripeSecretKey?: string; cdnBaseUrl: string; diff --git a/controlplane/src/core/services/Keycloak.ts b/controlplane/src/core/services/Keycloak.ts index ff7d1a7a63..a1b0c2a892 100644 --- a/controlplane/src/core/services/Keycloak.ts +++ b/controlplane/src/core/services/Keycloak.ts @@ -278,13 +278,13 @@ export default class Keycloak { }); if (orgGroups.length === 0) { - throw new Error(`Organization group '${organizationSlug}' not found`); + return; } const orgGroup = orgGroups.find((group) => group.name === organizationSlug); if (!orgGroup) { - throw new Error(`Organization group '${organizationSlug}' not found`); + return; } await this.client.groups.del({ diff --git a/controlplane/src/core/webhooks/PlatformWebhookService.ts b/controlplane/src/core/webhooks/PlatformWebhookService.ts index 10e7f13bb3..d147ead1e1 100644 --- a/controlplane/src/core/webhooks/PlatformWebhookService.ts +++ b/controlplane/src/core/webhooks/PlatformWebhookService.ts @@ -59,7 +59,7 @@ export class PlatformWebhookService implements IPlatformWebhookService { }); } - send(eventName: T, eventData: EventMap[T]) { + async send(eventName: T, eventData: EventMap[T]) { if (!this.url) { return; } @@ -73,7 +73,9 @@ export class PlatformWebhookService implements IPlatformWebhookService { }; // @TODO Use a queue to send the events - makeWebhookRequest(this.httpClient, data, this.url, this.key).catch((error: AxiosError) => { + try { + await makeWebhookRequest(this.httpClient, data, this.url, this.key); + } catch (error) { if (error instanceof AxiosError) { logger.error( { statusCode: error.response?.status, message: error.message }, @@ -82,7 +84,7 @@ export class PlatformWebhookService implements IPlatformWebhookService { } else { logger.error(error, 'Could not send platform webhook event'); } - }); + } } } diff --git a/controlplane/src/core/workers/AIGraphReadmeWorker.ts b/controlplane/src/core/workers/AIGraphReadmeWorker.ts index eb9acae480..05cc28c93c 100644 --- a/controlplane/src/core/workers/AIGraphReadmeWorker.ts +++ b/controlplane/src/core/workers/AIGraphReadmeWorker.ts @@ -5,6 +5,7 @@ import * as schema from '../../db/schema.js'; import { OpenAIGraphql } from '../openai-graphql/index.js'; import { SubgraphRepository } from '../repositories/SubgraphRepository.js'; import { FederatedGraphRepository } from '../repositories/FederatedGraphRepository.js'; +import { IQueue, IWorker } from './Worker.js'; const QueueName = 'ai.graph-readme-generator'; const WorkerName = 'AIGraphReadmeWorker'; @@ -15,7 +16,7 @@ export interface CreateReadmeInputEvent { type: 'subgraph' | 'federated_graph'; } -export class AIGraphReadmeQueue { +export class AIGraphReadmeQueue implements IQueue { private readonly queue: Queue; private readonly logger: pino.Logger; @@ -39,8 +40,8 @@ export class AIGraphReadmeQueue { }); } - public async addJob(job: CreateReadmeInputEvent) { - await this.queue.add(`targets/${job.targetId}`, job, { + public addJob(job: CreateReadmeInputEvent) { + return this.queue.add(`targets/${job.targetId}`, job, { removeOnComplete: { age: 3600, // keep up to 1 hour count: 100, // keep up to 100 jobs @@ -50,9 +51,17 @@ export class AIGraphReadmeQueue { }, }); } + + public removeJob(job: CreateReadmeInputEvent) { + return this.queue.remove(job.targetId); + } + + public getJob(job: CreateReadmeInputEvent) { + return this.queue.getJob(job.targetId); + } } -class AIGraphReadmeWorker { +class AIGraphReadmeWorker implements IWorker { private readonly openaiGraphql: OpenAIGraphql; constructor( diff --git a/controlplane/src/core/workers/DeactivateOrganizationWorker.ts b/controlplane/src/core/workers/DeactivateOrganizationWorker.ts new file mode 100644 index 0000000000..d1ee93cb3e --- /dev/null +++ b/controlplane/src/core/workers/DeactivateOrganizationWorker.ts @@ -0,0 +1,134 @@ +import { ConnectionOptions, Job, JobsOptions, Queue, Worker } from 'bullmq'; +import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; +import pino from 'pino'; +import * as schema from '../../db/schema.js'; +import { OrganizationRepository } from '../repositories/OrganizationRepository.js'; +import Keycloak from '../services/Keycloak.js'; +import { DeleteOrganizationQueue } from './DeleteOrganizationWorker.js'; +import { IQueue, IWorker } from './Worker.js'; + +const QueueName = 'organization.deactivate'; +const WorkerName = 'DeactivateOrganizationWorker'; + +export interface DeactivateOrganizationInput { + organizationId: string; + organizationSlug: string; + deactivationReason?: string; +} + +export class DeactivateOrganizationQueue implements IQueue { + private readonly queue: Queue; + private readonly logger: pino.Logger; + + constructor(log: pino.Logger, conn: ConnectionOptions) { + this.logger = log.child({ queue: QueueName }); + this.queue = new Queue(QueueName, { + connection: conn, + defaultJobOptions: { + removeOnComplete: { + age: 90 * 86_400, + }, + removeOnFail: { + age: 90 * 86_400, + }, + attempts: 6, + backoff: { + type: 'exponential', + delay: 112_000, + }, + }, + }); + + this.queue.on('error', (err) => { + this.logger.error(err, 'Queue error'); + }); + } + + public addJob(job: DeactivateOrganizationInput, opts?: Omit) { + this.logger.info('Adding deactivate job'); + return this.queue.add(job.organizationId, job, { + ...opts, + jobId: job.organizationId, + }); + } + + public removeJob(job: DeactivateOrganizationInput) { + return this.queue.remove(job.organizationId); + } + + public getJob(job: DeactivateOrganizationInput) { + return this.queue.getJob(job.organizationId); + } +} + +class DeactivateOrganizationWorker implements IWorker { + constructor( + private input: { + db: PostgresJsDatabase; + logger: pino.Logger; + keycloakClient: Keycloak; + keycloakRealm: string; + deleteOrganizationQueue: DeleteOrganizationQueue; + }, + ) { + this.input.logger = input.logger.child({ worker: WorkerName }); + } + + public async handler(job: Job) { + try { + this.input.logger.info('Processing deactivate job'); + const orgRepo = new OrganizationRepository(this.input.logger, this.input.db); + + await this.input.keycloakClient.authenticateClient(); + + const org = await orgRepo.bySlug(job.data.organizationSlug); + if (!org) { + throw new Error('Organization not found'); + } + + if (org.id !== job.data.organizationId) { + throw new Error('Id and slug mismatch'); + } + + await orgRepo.deactivateOrganization({ + organizationId: job.data.organizationId, + reason: job.data.deactivationReason, + keycloakClient: this.input.keycloakClient, + keycloakRealm: this.input.keycloakRealm, + deleteOrganizationQueue: this.input.deleteOrganizationQueue, + }); + } catch (err) { + this.input.logger.error( + { jobId: job.id, organizationId: job.data.organizationId, err }, + `Failed to deactivate organization`, + ); + throw err; + } + } +} + +export const createDeactivateOrganizationWorker = (input: { + redisConnection: ConnectionOptions; + db: PostgresJsDatabase; + logger: pino.Logger; + keycloakClient: Keycloak; + keycloakRealm: string; + deleteOrganizationQueue: DeleteOrganizationQueue; +}) => { + const log = input.logger.child({ worker: WorkerName }); + const worker = new Worker( + QueueName, + (job) => new DeactivateOrganizationWorker(input).handler(job), + { + connection: input.redisConnection, + concurrency: 10, + }, + ); + worker.on('stalled', (job) => { + log.warn({ joinId: job }, `Job stalled`); + }); + worker.on('error', (err) => { + log.error(err, 'Worker error'); + }); + return worker; +}; diff --git a/controlplane/src/core/workers/DeleteOrganizationWorker.ts b/controlplane/src/core/workers/DeleteOrganizationWorker.ts index 0955a94f26..88e3674558 100644 --- a/controlplane/src/core/workers/DeleteOrganizationWorker.ts +++ b/controlplane/src/core/workers/DeleteOrganizationWorker.ts @@ -6,6 +6,8 @@ import { OrganizationRepository } from '../repositories/OrganizationRepository.j import Keycloak from '../services/Keycloak.js'; import { OidcRepository } from '../repositories/OidcRepository.js'; import OidcProvider from '../services/OidcProvider.js'; +import { BlobStorage } from '../blobstorage/index.js'; +import { IQueue, IWorker } from './Worker.js'; const QueueName = 'organization.delete'; const WorkerName = 'DeleteOrganizationWorker'; @@ -14,7 +16,7 @@ export interface DeleteOrganizationInput { organizationId: string; } -export class DeleteOrganizationQueue { +export class DeleteOrganizationQueue implements IQueue { private readonly queue: Queue; private readonly logger: pino.Logger; @@ -23,12 +25,16 @@ export class DeleteOrganizationQueue { this.queue = new Queue(QueueName, { connection: conn, defaultJobOptions: { - removeOnComplete: true, - removeOnFail: true, - attempts: 3, + removeOnComplete: { + age: 90 * 86_400, + }, + removeOnFail: { + age: 90 * 86_400, + }, + attempts: 6, backoff: { type: 'exponential', - delay: 10_000, + delay: 112_000, }, }, }); @@ -54,7 +60,7 @@ export class DeleteOrganizationQueue { } } -class DeleteOrganizationWorker { +class DeleteOrganizationWorker implements IWorker { constructor( private input: { redisConnection: ConnectionOptions; @@ -62,6 +68,7 @@ class DeleteOrganizationWorker { logger: pino.Logger; keycloakClient: Keycloak; keycloakRealm: string; + blobStorage: BlobStorage; }, ) { this.input.logger = input.logger.child({ worker: WorkerName }); @@ -90,21 +97,17 @@ class DeleteOrganizationWorker { }); } + await orgRepo.deleteOrganization(job.data.organizationId, this.input.blobStorage); + await this.input.keycloakClient.deleteOrganizationGroup({ realm: this.input.keycloakRealm, organizationSlug: org.slug, }); - - await this.input.db.transaction(async (tx) => { - const orgRepo = new OrganizationRepository(this.input.logger, tx); - const oidcRepo = new OidcRepository(tx); - - await oidcRepo.deleteOidcProvider({ organizationId: job.data.organizationId }); - - await orgRepo.deleteOrganization(job.data.organizationId); - }); } catch (err) { - this.input.logger.error(err, `Failed to delete organization with id ${job.data.organizationId}`); + this.input.logger.error( + { jobId: job.id, organizationId: job.data.organizationId, err }, + `Failed to delete organization`, + ); throw err; } } @@ -116,6 +119,7 @@ export const createDeleteOrganizationWorker = (input: { logger: pino.Logger; keycloakClient: Keycloak; keycloakRealm: string; + blobStorage: BlobStorage; }) => { const log = input.logger.child({ worker: WorkerName }); const worker = new Worker( @@ -127,7 +131,7 @@ export const createDeleteOrganizationWorker = (input: { }, ); worker.on('stalled', (job) => { - log.warn(`Job ${job} stalled`); + log.warn({ joinId: job }, `Job stalled`); }); worker.on('error', (err) => { log.error(err, 'Worker error'); diff --git a/controlplane/src/core/workers/DeleteUserQueue.ts b/controlplane/src/core/workers/DeleteUserQueue.ts new file mode 100644 index 0000000000..10081db542 --- /dev/null +++ b/controlplane/src/core/workers/DeleteUserQueue.ts @@ -0,0 +1,145 @@ +import { PlatformEventName } from '@wundergraph/cosmo-connect/dist/notifications/events_pb'; +import { ConnectionOptions, Job, JobsOptions, Queue, Worker } from 'bullmq'; +import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; +import pino from 'pino'; +import * as schema from '../../db/schema.js'; +import { BlobStorage } from '../blobstorage/index.js'; +import { OrganizationRepository } from '../repositories/OrganizationRepository.js'; +import { UserRepository } from '../repositories/UserRepository.js'; +import Keycloak from '../services/Keycloak.js'; +import { PlatformWebhookService } from '../webhooks/PlatformWebhookService.js'; +import { IQueue, IWorker } from './Worker.js'; + +const QueueName = 'user.delete'; +const WorkerName = 'DeleteUserWorker'; + +export interface DeleteUserInput { + userId: string; + userEmail: string; +} + +export class DeleteUserQueue implements IQueue { + private readonly queue: Queue; + private readonly logger: pino.Logger; + + constructor(log: pino.Logger, conn: ConnectionOptions) { + this.logger = log.child({ queue: QueueName }); + this.queue = new Queue(QueueName, { + connection: conn, + defaultJobOptions: { + removeOnComplete: { + age: 90 * 86_400, + }, + removeOnFail: { + age: 90 * 86_400, + }, + attempts: 6, + backoff: { + type: 'exponential', + delay: 112_000, + }, + }, + }); + + this.queue.on('error', (err) => { + this.logger.error(err, 'Queue error'); + }); + } + + public addJob(job: DeleteUserInput, opts?: Omit) { + return this.queue.add(job.userId, job, { + ...opts, + jobId: job.userId, + }); + } + + public removeJob(job: DeleteUserInput) { + return this.queue.remove(job.userId); + } + + public getJob(job: DeleteUserInput) { + return this.queue.getJob(job.userId); + } +} + +class DeleteUserWorker implements IWorker { + constructor( + private input: { + redisConnection: ConnectionOptions; + db: PostgresJsDatabase; + logger: pino.Logger; + keycloakClient: Keycloak; + keycloakRealm: string; + blobStorage: BlobStorage; + platformWebhooks: PlatformWebhookService; + }, + ) { + this.input.logger = input.logger.child({ worker: WorkerName }); + } + + public async handler(job: Job) { + try { + const userRepo = new UserRepository(this.input.logger, this.input.db); + const orgRepo = new OrganizationRepository(this.input.logger, this.input.db); + + await this.input.keycloakClient.authenticateClient(); + + const user = await userRepo.byId(job.data.userId); + if (user) { + const { isSafe, soloOrganizations, unsafeOrganizations } = await orgRepo.canUserBeDeleted(job.data.userId); + + if (!isSafe) { + this.input.logger.info( + { + userId: job.data.userId, + soloOrganizations, + unsafeOrganizations, + }, + 'Unsafe to delete user', + ); + return; + } + + await userRepo.deleteUser( + { + id: job.data.userId, + keycloakClient: this.input.keycloakClient, + keycloakRealm: this.input.keycloakRealm, + }, + this.input.blobStorage, + ); + } + + await this.input.platformWebhooks.send(PlatformEventName.USER_DELETE_SUCCESS, { + user_id: job.data.userId, + user_email: job.data.userEmail, + }); + } catch (err) { + this.input.logger.error({ jobId: job.id, userId: job.data.userId, err }, `Failed to delete user`); + throw err; + } + } +} + +export const createDeleteUserWorker = (input: { + redisConnection: ConnectionOptions; + db: PostgresJsDatabase; + logger: pino.Logger; + keycloakClient: Keycloak; + keycloakRealm: string; + blobStorage: BlobStorage; + platformWebhooks: PlatformWebhookService; +}) => { + const log = input.logger.child({ worker: WorkerName }); + const worker = new Worker(QueueName, (job) => new DeleteUserWorker(input).handler(job), { + connection: input.redisConnection, + concurrency: 10, + }); + worker.on('stalled', (job) => { + log.warn({ joinId: job }, `Job stalled`); + }); + worker.on('error', (err) => { + log.error(err, 'Worker error'); + }); + return worker; +}; diff --git a/controlplane/src/core/workers/ReactivateOrganizationWorker.ts b/controlplane/src/core/workers/ReactivateOrganizationWorker.ts new file mode 100644 index 0000000000..24c20fdf1a --- /dev/null +++ b/controlplane/src/core/workers/ReactivateOrganizationWorker.ts @@ -0,0 +1,122 @@ +import { ConnectionOptions, Job, JobsOptions, Queue, Worker } from 'bullmq'; +import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; +import pino from 'pino'; +import * as schema from '../../db/schema.js'; +import { OrganizationRepository } from '../repositories/OrganizationRepository.js'; +import Keycloak from '../services/Keycloak.js'; +import { DeleteOrganizationQueue } from './DeleteOrganizationWorker.js'; +import { IQueue, IWorker } from './Worker.js'; + +const QueueName = 'organization.reactivate'; +const WorkerName = 'ReactivateOrganizationWorker'; + +export interface ReactivateOrganizationInput { + organizationId: string; + organizationSlug: string; +} + +export class ReactivateOrganizationQueue implements IQueue { + private readonly queue: Queue; + private readonly logger: pino.Logger; + + constructor(log: pino.Logger, conn: ConnectionOptions) { + this.logger = log.child({ queue: QueueName }); + this.queue = new Queue(QueueName, { + connection: conn, + defaultJobOptions: { + removeOnComplete: { + age: 90 * 86_400, + }, + removeOnFail: { + age: 90 * 86_400, + }, + attempts: 6, + backoff: { + type: 'exponential', + delay: 112_000, + }, + }, + }); + + this.queue.on('error', (err) => { + this.logger.error(err, 'Queue error'); + }); + } + + public addJob(job: ReactivateOrganizationInput, opts?: Omit) { + return this.queue.add(job.organizationId, job, { + ...opts, + jobId: job.organizationId, + }); + } + + public removeJob(job: ReactivateOrganizationInput) { + return this.queue.remove(job.organizationId); + } + + public getJob(job: ReactivateOrganizationInput) { + return this.queue.getJob(job.organizationId); + } +} + +class ReactivateOrganizationWorker implements IWorker { + constructor( + private input: { + db: PostgresJsDatabase; + logger: pino.Logger; + deleteOrganizationQueue: DeleteOrganizationQueue; + }, + ) { + this.input.logger = input.logger.child({ worker: WorkerName }); + } + + public async handler(job: Job) { + try { + const orgRepo = new OrganizationRepository(this.input.logger, this.input.db); + + const org = await orgRepo.bySlug(job.data.organizationSlug); + if (!org) { + throw new Error('Organization not found'); + } + + if (org.id !== job.data.organizationId) { + throw new Error('Id and slug mismatch'); + } + + await orgRepo.reactivateOrganization({ + organizationId: job.data.organizationId, + deleteOrganizationQueue: this.input.deleteOrganizationQueue, + }); + } catch (err) { + this.input.logger.error( + { jobId: job.id, organizationId: job.data.organizationId, err }, + `Failed to reactivate organization`, + ); + throw err; + } + } +} + +export const createReactivateOrganizationWorker = (input: { + redisConnection: ConnectionOptions; + db: PostgresJsDatabase; + logger: pino.Logger; + deleteOrganizationQueue: DeleteOrganizationQueue; +}) => { + const log = input.logger.child({ worker: WorkerName }); + const worker = new Worker( + QueueName, + (job) => new ReactivateOrganizationWorker(input).handler(job), + { + connection: input.redisConnection, + concurrency: 10, + }, + ); + worker.on('stalled', (job) => { + log.warn({ joinId: job }, `Job stalled`); + }); + worker.on('error', (err) => { + log.error(err, 'Worker error'); + }); + return worker; +}; diff --git a/controlplane/src/core/workers/Worker.ts b/controlplane/src/core/workers/Worker.ts new file mode 100644 index 0000000000..b268460a33 --- /dev/null +++ b/controlplane/src/core/workers/Worker.ts @@ -0,0 +1,11 @@ +import { Job, JobsOptions } from 'bullmq'; + +export interface IQueue { + addJob(job: T, opts?: Omit): Promise | undefined>; + removeJob(job: T): Promise; + getJob(job: T): Promise | undefined>; +} + +export interface IWorker { + handler(job: Job): Promise; +} diff --git a/controlplane/src/db/schema.ts b/controlplane/src/db/schema.ts index 1bbcbd63d3..c3a9df9510 100644 --- a/controlplane/src/db/schema.ts +++ b/controlplane/src/db/schema.ts @@ -219,7 +219,7 @@ export const subgraphs = pgTable( websocketSubprotocol: websocketSubprotocolEnum('websocket_subprotocol').notNull().default('auto'), // This is the latest valid schema of the subgraph. schemaVersionId: uuid('schema_version_id').references(() => schemaVersion.id, { - onDelete: 'set null', + onDelete: 'no action', }), targetId: uuid('target_id') .notNull() diff --git a/controlplane/test/deactivate-org.test.ts b/controlplane/test/deactivate-org.test.ts index 48a60f1551..6af2805af4 100644 --- a/controlplane/test/deactivate-org.test.ts +++ b/controlplane/test/deactivate-org.test.ts @@ -5,6 +5,7 @@ import { OidcRepository } from '../src/core/repositories/OidcRepository.js'; import { OrganizationRepository } from '../src/core/repositories/OrganizationRepository.js'; import { afterAllSetup, beforeAllSetup, genID, TestUser } from '../src/core/test-util.js'; import { createDeleteOrganizationWorker } from '../src/core/workers/DeleteOrganizationWorker.js'; +import { createReactivateOrganizationWorker } from '../src/core/workers/ReactivateOrganizationWorker.js'; import { SetupTest } from './test-util.js'; let dbname = ''; @@ -19,7 +20,9 @@ describe('Deactivate Organization', (ctx) => { }); test('Should deactivate org and delete after scheduled', async (testContext) => { - const { client, server, keycloakClient, realm, queues, users, authenticator } = await SetupTest({ dbname }); + const { client, server, keycloakClient, realm, queues, users, authenticator, blobStorage } = await SetupTest({ + dbname, + }); const mainUserContext = users[TestUser.adminAliceCompanyA]; const orgName = genID(); @@ -63,6 +66,7 @@ describe('Deactivate Organization', (ctx) => { logger: server.log, keycloakClient, keycloakRealm: realm, + blobStorage, }); const job = await orgRepo.deactivateOrganization({ @@ -92,7 +96,9 @@ describe('Deactivate Organization', (ctx) => { }); test('Should reactivate org and remove the scheduled deletion', async (testContext) => { - const { client, server, keycloakClient, realm, queues, users, authenticator } = await SetupTest({ dbname }); + const { client, server, keycloakClient, realm, queues, users, authenticator, blobStorage } = await SetupTest({ + dbname, + }); const mainUserContext = users[TestUser.adminAliceCompanyA]; const orgName = genID(); @@ -112,12 +118,13 @@ describe('Deactivate Organization', (ctx) => { organizationSlug: org!.slug, }); - const worker = createDeleteOrganizationWorker({ + const deleteOrgWorker = createDeleteOrganizationWorker({ redisConnection: server.redisForWorker, db: server.db, logger: server.log, keycloakClient, keycloakRealm: realm, + blobStorage, }); await orgRepo.deactivateOrganization({ @@ -135,11 +142,20 @@ describe('Deactivate Organization', (ctx) => { const deactivatedOrg = await orgRepo.bySlug(orgName); expect(deactivatedOrg?.deactivation).toBeDefined(); - await orgRepo.reactivateOrganization({ - organizationId: org!.id, + const reactivateWorker = createReactivateOrganizationWorker({ + redisConnection: server.redisForWorker, + db: server.db, + logger: server.log, deleteOrganizationQueue: queues.deleteOrganizationQueue, }); + const reactivateJob = await queues.reactivateOrganizationQueue.addJob({ + organizationId: org!.id, + organizationSlug: org!.slug, + }); + + await reactivateJob.waitUntilFinished(new QueueEvents(reactivateJob.queueName)); + const removedJob = await queues.deleteOrganizationQueue.getJob({ organizationId: org!.id, }); @@ -148,7 +164,8 @@ describe('Deactivate Organization', (ctx) => { const reactivatedOrg = await orgRepo.bySlug(orgName); expect(reactivatedOrg?.deactivation).toBeUndefined(); - await worker.close(); + await deleteOrgWorker.close(); + await reactivateWorker.close(); await server.close(); }); diff --git a/controlplane/test/delete-organization.test.ts b/controlplane/test/delete-organization.test.ts new file mode 100644 index 0000000000..64e8483280 --- /dev/null +++ b/controlplane/test/delete-organization.test.ts @@ -0,0 +1,143 @@ +import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb'; +import { joinLabel } from '@wundergraph/cosmo-shared'; +import { afterAll, beforeAll, describe, expect, test } from 'vitest'; +import { OidcRepository } from '../src/core/repositories/OidcRepository.js'; +import { OrganizationRepository } from '../src/core/repositories/OrganizationRepository.js'; +import { afterAllSetup, beforeAllSetup, genID, genUniqueLabel, TestUser } from '../src/core/test-util.js'; +import { createFederatedGraph, createThenPublishSubgraph, DEFAULT_NAMESPACE, SetupTest } from './test-util.js'; + +let dbname = ''; + +describe('Delete Organization', (ctx) => { + beforeAll(async () => { + dbname = await beforeAllSetup(); + }); + + afterAll(async () => { + await afterAllSetup(dbname); + }); + + test('Should delete all targets when deleting org', async (testContext) => { + const { client, server, users, authenticator, blobStorage } = await SetupTest({ + dbname, + }); + const mainUserContext = users[TestUser.adminAliceCompanyA]; + + const orgName = genID(); + await client.createOrganization({ + name: orgName, + slug: orgName, + }); + + const orgRepo = new OrganizationRepository(server.log, server.db); + const org = await orgRepo.bySlug(orgName); + expect(org).toBeDefined(); + + authenticator.changeUserWithSuppliedContext({ + ...mainUserContext, + organizationId: org!.id, + organizationName: org!.name, + organizationSlug: org!.slug, + }); + + const subgraphName = genID('subgraph'); + const fedGraphName = genID('fedGraph'); + const label = genUniqueLabel('label'); + + const subgraphSchemaSDL = 'type Query { hello: String!, hi: String! @tag(name: "test") }'; + await createThenPublishSubgraph( + client, + subgraphName, + DEFAULT_NAMESPACE, + subgraphSchemaSDL, + [label], + 'http://localhost:8082', + ); + + await createFederatedGraph(client, fedGraphName, DEFAULT_NAMESPACE, [joinLabel(label)], 'http://localhost:8080'); + + const graphRes = await client.getFederatedGraphByName({ + name: fedGraphName, + namespace: 'default', + }); + expect(graphRes.response?.code).toBe(EnumStatusCode.OK); + expect(graphRes.subgraphs.length).toBe(1); + + const graphKey = `${org!.id}/${graphRes.graph?.id}/routerconfigs/latest.json`; + + expect(blobStorage.keys().includes(graphKey)).toEqual(true); + + const deleteOrgRes = await client.deleteOrganization({ + userID: mainUserContext.userId, + }); + expect(deleteOrgRes.response?.code).toBe(EnumStatusCode.OK); + + const graphsRes = await client.getFederatedGraphs({}); + expect(graphsRes.response?.code).toBe(EnumStatusCode.OK); + expect(graphsRes.graphs.length).toBe(0); + + const subgraphsRes = await client.getSubgraphs({}); + expect(subgraphsRes.response?.code).toBe(EnumStatusCode.OK); + expect(subgraphsRes.graphs.length).toBe(0); + + const orgAfterDeletion = await orgRepo.bySlug(orgName); + expect(orgAfterDeletion).toBeNull(); + + expect(blobStorage.keys().includes(graphKey)).toEqual(false); + await server.close(); + }); + + test('Should delete OIDC when deleting org', async (testContext) => { + const { client, server, keycloakClient, realm, users, authenticator } = await SetupTest({ + dbname, + }); + const mainUserContext = users[TestUser.adminAliceCompanyA]; + + const orgName = genID(); + await client.createOrganization({ + name: orgName, + slug: orgName, + }); + + const orgRepo = new OrganizationRepository(server.log, server.db); + const org = await orgRepo.bySlug(orgName); + expect(org).toBeDefined(); + + authenticator.changeUserWithSuppliedContext({ + ...mainUserContext, + organizationId: org!.id, + organizationName: org!.name, + organizationSlug: org!.slug, + }); + + const createOIDCRes = await client.createOIDCProvider({ + clientID: '123', + clientSecrect: '345', + discoveryEndpoint: `http://localhost:8080/realms/${realm}/.well-known/openid-configuration`, + mappers: [], + }); + expect(createOIDCRes.response?.code).toBe(EnumStatusCode.OK); + + const oidcRepo = new OidcRepository(server.db); + const provider = await oidcRepo.getOidcProvider({ organizationId: org!.id }); + expect(provider).toBeDefined(); + + const deleteOrgRes = await client.deleteOrganization({ + userID: mainUserContext.userId, + }); + expect(deleteOrgRes.response?.code).toBe(EnumStatusCode.OK); + + const provider2 = await oidcRepo.getOidcProvider({ organizationId: org!.id }); + expect(provider2).toBeUndefined(); + const idp2 = await keycloakClient.client.identityProviders.findOne({ + alias: provider!.alias, + realm, + }); + expect(idp2).toBeNull(); + + const orgAfterDeletion = await orgRepo.bySlug(orgName); + expect(orgAfterDeletion).toBeNull(); + + await server.close(); + }); +}); diff --git a/controlplane/test/test-util.ts b/controlplane/test/test-util.ts index 692687ce5e..e08af67b33 100644 --- a/controlplane/test/test-util.ts +++ b/controlplane/test/test-util.ts @@ -40,6 +40,9 @@ import { DeleteOrganizationQueue } from '../src/core/workers/DeleteOrganizationW import * as schema from '../src/db/schema.js'; import { FeatureIds, Label } from '../src/types/index.js'; import { NewBillingPlan } from '../src/db/models.js'; +import { DeactivateOrganizationQueue } from '../src/core/workers/DeactivateOrganizationWorker.js'; +import { DeleteUserQueue } from '../src/core/workers/DeleteUserQueue.js'; +import { ReactivateOrganizationQueue } from '../src/core/workers/ReactivateOrganizationWorker.js'; export const DEFAULT_ROUTER_URL = 'http://localhost:3002'; export const DEFAULT_SUBGRAPH_URL_ONE = 'http://localhost:4001'; @@ -125,6 +128,9 @@ export const SetupTest = async function ({ const readmeQueue = new AIGraphReadmeQueue(log, server.redisForQueue); const deleteOrganizationQueue = new DeleteOrganizationQueue(log, server.redisForQueue); + const deactivateOrganizationQueue = new DeactivateOrganizationQueue(log, server.redisForQueue); + const deleteUserQueue = new DeleteUserQueue(log, server.redisForQueue); + const reactivateOrganizationQueue = new ReactivateOrganizationQueue(log, server.redisForQueue); const blobStorage = new InMemoryBlobStorage(); await server.register(fastifyConnectPlugin, { @@ -151,6 +157,9 @@ export const SetupTest = async function ({ queues: { readmeQueue, deleteOrganizationQueue, + deactivateOrganizationQueue, + reactivateOrganizationQueue, + deleteUserQueue, }, }), }); @@ -318,8 +327,11 @@ export const SetupTest = async function ({ authenticator, realm, queues: { - deleteOrganizationQueue, readmeQueue, + deleteOrganizationQueue, + deactivateOrganizationQueue, + deleteUserQueue, + reactivateOrganizationQueue, }, }; }; diff --git a/helm/cosmo/README.md b/helm/cosmo/README.md index 225f51380c..24ed822383 100644 --- a/helm/cosmo/README.md +++ b/helm/cosmo/README.md @@ -85,7 +85,7 @@ This is the official Helm Chart for WunderGraph Cosmo - The Full Lifecycle Graph | controlplane.configuration.smtp.requireTls | bool | `true` | Forces the client to use STARTTLS. Default is true. | | controlplane.configuration.smtp.secure | bool | `true` | Defines if the connection should use SSL. Default is true. | | controlplane.configuration.smtp.username | string | `""` | The username to use. Default is "". | -| controlplane.jobs | object | `{"activateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"},"clickhouseMigration":{"additionalLabels":{}},"databaseMigration":{"additionalLabels":{}},"deactivateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","reason":"","slug":"foo"},"deleteUser":{"additionalLabels":{},"enabled":false,"id":"123"},"seedOrganization":{"additionalLabels":{}}}` | Configure jobs to be executed in the control plane | +| controlplane.jobs | object | `{"activateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"},"clickhouseMigration":{"additionalLabels":{}},"databaseMigration":{"additionalLabels":{}},"deactivateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","reason":"","slug":"foo"},"deleteUser":{"additionalLabels":{},"email":"foo@wundergraph.com","enabled":false,"id":"123"},"seedOrganization":{"additionalLabels":{}}}` | Configure jobs to be executed in the control plane | | controlplane.jobs.activateOrganization | object | `{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"}` | Used to activate an organization and remove the scheduled deletion | | controlplane.jobs.activateOrganization.additionalLabels | object | `{}` | Adds additional labels to the job | | controlplane.jobs.activateOrganization.enabled | bool | `false` | Enables the job to be run | @@ -99,8 +99,9 @@ This is the official Helm Chart for WunderGraph Cosmo - The Full Lifecycle Graph | controlplane.jobs.deactivateOrganization.id | string | `"123"` | The unique identifier of the organization | | controlplane.jobs.deactivateOrganization.reason | string | `""` | The reason for deactivation | | controlplane.jobs.deactivateOrganization.slug | string | `"foo"` | The slug of the organization | -| controlplane.jobs.deleteUser | object | `{"additionalLabels":{},"enabled":false,"id":"123"}` | Used to delete the user | +| controlplane.jobs.deleteUser | object | `{"additionalLabels":{},"email":"foo@wundergraph.com","enabled":false,"id":"123"}` | Used to delete the user | | controlplane.jobs.deleteUser.additionalLabels | object | `{}` | Adds additional labels to the job | +| controlplane.jobs.deleteUser.email | string | `"foo@wundergraph.com"` | The email of the user | | controlplane.jobs.deleteUser.enabled | bool | `false` | Enables the job to be run | | controlplane.jobs.deleteUser.id | string | `"123"` | The unique identifier of the user | | controlplane.jobs.seedOrganization.additionalLabels | object | `{}` | Adds additional labels to the job (see: .Values.global.seed) | diff --git a/helm/cosmo/charts/controlplane/README.md b/helm/cosmo/charts/controlplane/README.md index 04bdd73972..203864d046 100644 --- a/helm/cosmo/charts/controlplane/README.md +++ b/helm/cosmo/charts/controlplane/README.md @@ -79,7 +79,7 @@ WunderGraph Cosmo Controlplane | imagePullSecrets | list | `[]` | | | ingress.hosts | string | `nil` | | | ingress.tls | list | `[]` | | -| jobs | object | `{"activateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"},"clickhouseMigration":{"additionalLabels":{}},"databaseMigration":{"additionalLabels":{}},"deactivateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","reason":"","slug":"foo"},"deleteUser":{"additionalLabels":{},"enabled":false,"id":"123"},"seedOrganization":{"additionalLabels":{}}}` | Configure jobs to be executed in the control plane | +| jobs | object | `{"activateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"},"clickhouseMigration":{"additionalLabels":{}},"databaseMigration":{"additionalLabels":{}},"deactivateOrganization":{"additionalLabels":{},"enabled":false,"id":"123","reason":"","slug":"foo"},"deleteUser":{"additionalLabels":{},"email":"foo@wundergraph.com","enabled":false,"id":"123"},"seedOrganization":{"additionalLabels":{}}}` | Configure jobs to be executed in the control plane | | jobs.activateOrganization | object | `{"additionalLabels":{},"enabled":false,"id":"123","slug":"foo"}` | Used to activate an organization and remove the scheduled deletion | | jobs.activateOrganization.additionalLabels | object | `{}` | Adds additional labels to the job | | jobs.activateOrganization.enabled | bool | `false` | Enables the job to be run | @@ -93,8 +93,9 @@ WunderGraph Cosmo Controlplane | jobs.deactivateOrganization.id | string | `"123"` | The unique identifier of the organization | | jobs.deactivateOrganization.reason | string | `""` | The reason for deactivation | | jobs.deactivateOrganization.slug | string | `"foo"` | The slug of the organization | -| jobs.deleteUser | object | `{"additionalLabels":{},"enabled":false,"id":"123"}` | Used to delete the user | +| jobs.deleteUser | object | `{"additionalLabels":{},"email":"foo@wundergraph.com","enabled":false,"id":"123"}` | Used to delete the user | | jobs.deleteUser.additionalLabels | object | `{}` | Adds additional labels to the job | +| jobs.deleteUser.email | string | `"foo@wundergraph.com"` | The email of the user | | jobs.deleteUser.enabled | bool | `false` | Enables the job to be run | | jobs.deleteUser.id | string | `"123"` | The unique identifier of the user | | jobs.seedOrganization.additionalLabels | object | `{}` | Adds additional labels to the job (see: .Values.global.seed) | diff --git a/helm/cosmo/charts/controlplane/templates/activate-organization.yaml b/helm/cosmo/charts/controlplane/templates/activate-organization.yaml index b4d4514f8a..53ab28046d 100644 --- a/helm/cosmo/charts/controlplane/templates/activate-organization.yaml +++ b/helm/cosmo/charts/controlplane/templates/activate-organization.yaml @@ -38,64 +38,6 @@ spec: image: "{{ include "controlplane.image" . }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - - name: KC_REALM - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakRealm - - name: KC_LOGIN_REALM - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakLoginRealm - - name: KC_API_URL - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakApiUrl - - name: KC_ADMIN_USER - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: keycloakAdminUser - - name: KC_ADMIN_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: keycloakAdminPassword - - name: KC_CLIENT_ID - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakClientId - - - name: DB_URL - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseUrl - {{- if .Values.configuration.databaseTlsCert }} - - name: DB_TLS_CERT - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsCert - {{- end }} - {{- if .Values.configuration.databaseTlsCa }} - - name: DB_TLS_CA - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsCa - {{- end }} - {{- if .Values.configuration.databaseTlsKey }} - - name: DB_TLS_KEY - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsKey - {{- end }} - - name: REDIS_HOST valueFrom: configMapKeyRef: diff --git a/helm/cosmo/charts/controlplane/templates/deactivate-organization.yaml b/helm/cosmo/charts/controlplane/templates/deactivate-organization.yaml index e362fe96f1..76d296c0ec 100644 --- a/helm/cosmo/charts/controlplane/templates/deactivate-organization.yaml +++ b/helm/cosmo/charts/controlplane/templates/deactivate-organization.yaml @@ -38,64 +38,6 @@ spec: image: "{{ include "controlplane.image" . }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - - name: KC_REALM - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakRealm - - name: KC_LOGIN_REALM - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakLoginRealm - - name: KC_API_URL - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakApiUrl - - name: KC_ADMIN_USER - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: keycloakAdminUser - - name: KC_ADMIN_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: keycloakAdminPassword - - name: KC_CLIENT_ID - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakClientId - - - name: DB_URL - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseUrl - {{- if .Values.configuration.databaseTlsCert }} - - name: DB_TLS_CERT - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsCert - {{- end }} - {{- if .Values.configuration.databaseTlsCa }} - - name: DB_TLS_CA - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsCa - {{- end }} - {{- if .Values.configuration.databaseTlsKey }} - - name: DB_TLS_KEY - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsKey - {{- end }} - - name: REDIS_HOST valueFrom: configMapKeyRef: diff --git a/helm/cosmo/charts/controlplane/templates/delete-user.yaml b/helm/cosmo/charts/controlplane/templates/delete-user.yaml index 4414b1bd00..424f7b71e5 100644 --- a/helm/cosmo/charts/controlplane/templates/delete-user.yaml +++ b/helm/cosmo/charts/controlplane/templates/delete-user.yaml @@ -37,81 +37,49 @@ spec: image: "{{ include "controlplane.image" . }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - - name: KC_REALM + - name: REDIS_HOST valueFrom: configMapKeyRef: name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakRealm - - name: KC_LOGIN_REALM + key: redisHost + - name: REDIS_PORT valueFrom: configMapKeyRef: name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakLoginRealm - - name: KC_API_URL - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakApiUrl - - name: KC_ADMIN_USER - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: keycloakAdminUser - - name: KC_ADMIN_PASSWORD + key: redisPort + {{- if .Values.configuration.redisPassword }} + - name: REDIS_PASSWORD valueFrom: secretKeyRef: name: {{ include "controlplane.secretName" . }} - key: keycloakAdminPassword - - name: KC_CLIENT_ID - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: keycloakClientId - - - name: DB_URL - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseUrl - {{- if .Values.configuration.databaseTlsCert }} - - name: DB_TLS_CERT - valueFrom: - secretKeyRef: - name: {{ include "controlplane.secretName" . }} - key: databaseTlsCert + key: redisPassword {{- end }} - {{- if .Values.configuration.databaseTlsCa }} - - name: DB_TLS_CA + {{- if .Values.configuration.redisTlsCert }} + - name: REDIS_TLS_CERT valueFrom: secretKeyRef: name: {{ include "controlplane.secretName" . }} - key: databaseTlsCa + key: redisTlsCert {{- end }} - {{- if .Values.configuration.databaseTlsKey }} - - name: DB_TLS_KEY + {{- if .Values.configuration.redisTlsKey }} + - name: REDIS_TLS_KEY valueFrom: secretKeyRef: name: {{ include "controlplane.secretName" . }} - key: databaseTlsKey - {{- end }} - - {{- if .Values.configuration.webhookUrl }} - - name: WEBHOOK_URL - valueFrom: - configMapKeyRef: - name: {{ include "controlplane.fullname" . }}-configmap - key: webhookUrl + key: redisTlsKey {{- end }} - {{- if .Values.configuration.webhookSecret }} - - name: WEBHOOK_SECRET + {{- if .Values.configuration.redisTlsCa }} + - name: REDIS_TLS_CA valueFrom: secretKeyRef: name: {{ include "controlplane.secretName" . }} - key: webhookSecret + key: redisTlsCa {{- end }} - name: USER_ID value: "{{ .Values.jobs.deleteUser.id }}" + - name: USER_EMAIL + value: "{{ .Values.jobs.deleteUser.email }}" args: - "/app/dist/bin/delete-user.js" {{- end }} \ No newline at end of file diff --git a/helm/cosmo/charts/controlplane/values.yaml b/helm/cosmo/charts/controlplane/values.yaml index 05ddbed877..2fd18684dd 100644 --- a/helm/cosmo/charts/controlplane/values.yaml +++ b/helm/cosmo/charts/controlplane/values.yaml @@ -276,6 +276,8 @@ jobs: enabled: false # -- The unique identifier of the user id: '123' + # -- The email of the user + email: 'foo@wundergraph.com' clickhouseMigration: # -- Adds additional labels to the clickhouse migration job (see: .Values.global.otelcollector) additionalLabels: {} diff --git a/helm/cosmo/values.yaml b/helm/cosmo/values.yaml index 68b6701ac6..b49f233b5b 100644 --- a/helm/cosmo/values.yaml +++ b/helm/cosmo/values.yaml @@ -171,6 +171,8 @@ controlplane: enabled: false # -- The unique identifier of the user id: '123' + # -- The email of the user + email: 'foo@wundergraph.com' clickhouseMigration: # -- Adds additional labels to the clickhouse migration job (see: .Values.global.otelcollector) additionalLabels: {} diff --git a/studio/src/pages/[organizationSlug]/settings.tsx b/studio/src/pages/[organizationSlug]/settings.tsx index c79b40651c..77a7f80069 100644 --- a/studio/src/pages/[organizationSlug]/settings.tsx +++ b/studio/src/pages/[organizationSlug]/settings.tsx @@ -1453,36 +1453,34 @@ const DeleteOrganization = () => { mode: "onChange", }); - const { mutate, isPending } = useMutation(deleteOrganization); - const { toast } = useToast(); + const { mutate, isPending } = useMutation(deleteOrganization, { + onSuccess: (d) => { + if (d.response?.code === EnumStatusCode.OK) { + router.reload(); + toast({ + description: "Deleted the organization succesfully.", + duration: 3000, + }); + } else if (d.response?.details) { + toast({ description: d.response.details, duration: 3000 }); + } + setOpen(false); + }, + onError: (error) => { + toast({ + description: "Could not delete the organization. Please try again.", + duration: 3000, + }); + setOpen(false); + }, + }); + const handleDeleteOrg = () => { - mutate( - { - userID: user?.id, - }, - { - onSuccess: (d) => { - if (d.response?.code === EnumStatusCode.OK) { - router.reload(); - toast({ - description: "Deleted the organization succesfully.", - duration: 3000, - }); - } else if (d.response?.details) { - toast({ description: d.response.details, duration: 3000 }); - } - }, - onError: (error) => { - toast({ - description: "Could not delete the organization. Please try again.", - duration: 3000, - }); - }, - }, - ); - setOpen(false); + mutate({ + userID: user?.id, + }); }; return (