-- Normalize all plugin secret/connection refs out of JSON columns-- into proper relational shape: graphql, openapi, mcp.-- pg port of apps/local/drizzle/0007_normalize_plugin_secret_refs.sql.-- (google-discovery is local-only — not in cloud's plugin list.) -- ============================================================-- graphql-- ============================================================ -- Normalize graphql plugin: move secret/connection refs out of JSON-- columns into proper relational shape so usagesForSecret /-- usagesForConnection are one indexed SELECT instead of a JSON scan.-- pg port of apps/local/drizzle/0007_normalize_graphql.sql. CREATE TABLE "graphql_source_header" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "graphql_source_header_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "graphql_source_header_scope_id_idx" ON "graphql_source_header" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "graphql_source_header_source_id_idx" ON "graphql_source_header" USING btree ("source_id");--> statement-breakpointCREATE INDEX "graphql_source_header_secret_id_idx" ON "graphql_source_header" USING btree ("secret_id");--> statement-breakpoint CREATE TABLE "graphql_source_query_param" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "graphql_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "graphql_source_query_param_scope_id_idx" ON "graphql_source_query_param" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "graphql_source_query_param_source_id_idx" ON "graphql_source_query_param" USING btree ("source_id");--> statement-breakpointCREATE INDEX "graphql_source_query_param_secret_id_idx" ON "graphql_source_query_param" USING btree ("secret_id");--> statement-breakpoint -- New auth columns. `auth_kind` defaults to "none" so existing rows that-- predate this migration are valid even if the json was null.ALTER TABLE "graphql_source" ADD COLUMN "auth_kind" text DEFAULT 'none' NOT NULL;--> statement-breakpointALTER TABLE "graphql_source" ADD COLUMN "auth_connection_id" text;--> statement-breakpointCREATE INDEX "graphql_source_auth_connection_id_idx" ON "graphql_source" USING btree ("auth_connection_id");--> statement-breakpoint -- Backfill auth from the JSON column. Missing keys yield NULL, so a row-- with auth=NULL or kind="none" leaves auth_connection_id NULL and-- auth_kind defaulted to "none".UPDATE "graphql_source"SET "auth_kind" = COALESCE("auth"->>'kind', 'none'), "auth_connection_id" = "auth"->>'connectionId'WHERE "auth" IS NOT NULL;--> statement-breakpoint -- Backfill headers. For each (source, header_name) pair: if the value-- is a json object with .secretId, write a kind=secret row; otherwise-- write a kind=text row with the literal string. jsonb_each iterates-- the keys of the headers object.INSERT INTO "graphql_source_header" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']', s."id", h.key, CASE WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL ENDFROM "graphql_source" s, jsonb_each(s."headers") hWHERE s."headers" IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint -- Same for query_params.INSERT INTO "graphql_source_query_param" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']', s."id", q.key, CASE WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL ENDFROM "graphql_source" s, jsonb_each(s."query_params") qWHERE s."query_params" IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint ALTER TABLE "graphql_source" DROP COLUMN "headers";--> statement-breakpointALTER TABLE "graphql_source" DROP COLUMN "query_params";--> statement-breakpointALTER TABLE "graphql_source" DROP COLUMN "auth"; --> statement-breakpoint -- ============================================================-- openapi-- ============================================================ -- Normalize openapi plugin: move every direct secret/connection ref out-- of JSON columns into proper relational shape. pg port of-- apps/local/drizzle/0008_normalize_openapi.sql. CREATE TABLE "openapi_source_query_param" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "openapi_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "openapi_source_query_param_scope_id_idx" ON "openapi_source_query_param" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "openapi_source_query_param_source_id_idx" ON "openapi_source_query_param" USING btree ("source_id");--> statement-breakpointCREATE INDEX "openapi_source_query_param_secret_id_idx" ON "openapi_source_query_param" USING btree ("secret_id");--> statement-breakpoint CREATE TABLE "openapi_source_spec_fetch_header" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "openapi_source_spec_fetch_header_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_header_scope_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_header_source_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("source_id");--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_header_secret_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("secret_id");--> statement-breakpoint CREATE TABLE "openapi_source_spec_fetch_query_param" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "openapi_source_spec_fetch_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_query_param_scope_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_query_param_source_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("source_id");--> statement-breakpointCREATE INDEX "openapi_source_spec_fetch_query_param_secret_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("secret_id");--> statement-breakpoint -- New columns on openapi_source_binding to flatten the value json.-- `kind` defaults to 'text' so the ALTER works on existing rows; the-- backfill below stamps the real value.ALTER TABLE "openapi_source_binding" ADD COLUMN "kind" text DEFAULT 'text' NOT NULL;--> statement-breakpointALTER TABLE "openapi_source_binding" ADD COLUMN "secret_id" text;--> statement-breakpointALTER TABLE "openapi_source_binding" ADD COLUMN "connection_id" text;--> statement-breakpointALTER TABLE "openapi_source_binding" ADD COLUMN "text_value" text;--> statement-breakpointCREATE INDEX "openapi_source_binding_secret_id_idx" ON "openapi_source_binding" USING btree ("secret_id");--> statement-breakpointCREATE INDEX "openapi_source_binding_connection_id_idx" ON "openapi_source_binding" USING btree ("connection_id");--> statement-breakpoint UPDATE "openapi_source_binding"SET "kind" = COALESCE("value"->>'kind', 'text'), "secret_id" = CASE WHEN "value"->>'kind' = 'secret' THEN "value"->>'secretId' ELSE NULL END, "connection_id" = CASE WHEN "value"->>'kind' = 'connection' THEN "value"->>'connectionId' ELSE NULL END, "text_value" = CASE WHEN "value"->>'kind' = 'text' THEN "value"->>'text' ELSE NULL ENDWHERE "value" IS NOT NULL;--> statement-breakpoint INSERT INTO "openapi_source_query_param" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']', s."id", q.key, CASE WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL ENDFROM "openapi_source" s, jsonb_each(s."query_params") qWHERE s."query_params" IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint INSERT INTO "openapi_source_spec_fetch_header" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']', s."id", h.key, CASE WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL ENDFROM "openapi_source" s, jsonb_each(s."invocation_config"->'specFetchCredentials'->'headers') hWHERE s."invocation_config"->'specFetchCredentials'->'headers' IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint INSERT INTO "openapi_source_spec_fetch_query_param" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']', s."id", q.key, CASE WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL ENDFROM "openapi_source" s, jsonb_each(s."invocation_config"->'specFetchCredentials'->'queryParams') qWHERE s."invocation_config"->'specFetchCredentials'->'queryParams' IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint -- Preserve any legacy OAuth payload from invocation_config.oauth2 into-- the still-existing oauth2 column before we drop invocation_config.-- migrateLegacyConnections runs after drizzle migrations and reads-- oauth2 to detect the legacy shape; without this, rows that only had-- their OAuth payload under invocation_config.oauth2 would lose it.UPDATE "openapi_source"SET "oauth2" = "invocation_config"->'oauth2'WHERE "oauth2" IS NULL AND "invocation_config"->'oauth2' IS NOT NULL;--> statement-breakpoint ALTER TABLE "openapi_source_binding" DROP COLUMN "value";--> statement-breakpointALTER TABLE "openapi_source" DROP COLUMN "query_params";--> statement-breakpointALTER TABLE "openapi_source" DROP COLUMN "invocation_config"; --> statement-breakpoint -- ============================================================-- mcp-- ============================================================ -- Normalize mcp plugin: lift the McpConnectionAuth secret/connection-- refs and the SecretBackedMap headers/query_params out of-- mcp_source.config JSON into proper columns / child tables. pg port-- of apps/local/drizzle/0009_normalize_mcp.sql. CREATE TABLE "mcp_source_header" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "mcp_source_header_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "mcp_source_header_scope_id_idx" ON "mcp_source_header" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "mcp_source_header_source_id_idx" ON "mcp_source_header" USING btree ("source_id");--> statement-breakpointCREATE INDEX "mcp_source_header_secret_id_idx" ON "mcp_source_header" USING btree ("secret_id");--> statement-breakpoint CREATE TABLE "mcp_source_query_param" ( "id" text NOT NULL, "scope_id" text NOT NULL, "source_id" text NOT NULL, "name" text NOT NULL, "kind" text NOT NULL, "text_value" text, "secret_id" text, "secret_prefix" text, CONSTRAINT "mcp_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id"));--> statement-breakpointCREATE INDEX "mcp_source_query_param_scope_id_idx" ON "mcp_source_query_param" USING btree ("scope_id");--> statement-breakpointCREATE INDEX "mcp_source_query_param_source_id_idx" ON "mcp_source_query_param" USING btree ("source_id");--> statement-breakpointCREATE INDEX "mcp_source_query_param_secret_id_idx" ON "mcp_source_query_param" USING btree ("secret_id");--> statement-breakpoint ALTER TABLE "mcp_source" ADD COLUMN "auth_kind" text DEFAULT 'none' NOT NULL;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_header_name" text;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_secret_id" text;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_secret_prefix" text;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_connection_id" text;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_client_id_secret_id" text;--> statement-breakpointALTER TABLE "mcp_source" ADD COLUMN "auth_client_secret_secret_id" text;--> statement-breakpointCREATE INDEX "mcp_source_auth_secret_id_idx" ON "mcp_source" USING btree ("auth_secret_id");--> statement-breakpointCREATE INDEX "mcp_source_auth_connection_id_idx" ON "mcp_source" USING btree ("auth_connection_id");--> statement-breakpointCREATE INDEX "mcp_source_auth_client_id_secret_id_idx" ON "mcp_source" USING btree ("auth_client_id_secret_id");--> statement-breakpointCREATE INDEX "mcp_source_auth_client_secret_secret_id_idx" ON "mcp_source" USING btree ("auth_client_secret_secret_id");--> statement-breakpoint -- Only update rows with explicitly current-shape auth (kind=header w/-- secretId, or kind=oauth2 w/ connectionId). Legacy inline-OAuth rows-- are left untouched so the post-migrate migrateLegacyConnections-- script can convert them to a Connection.UPDATE "mcp_source"SET "auth_kind" = "config"#>>'{auth,kind}', "auth_header_name" = "config"#>>'{auth,headerName}', "auth_secret_id" = "config"#>>'{auth,secretId}', "auth_secret_prefix" = "config"#>>'{auth,prefix}', "auth_connection_id" = "config"#>>'{auth,connectionId}', "auth_client_id_secret_id" = "config"#>>'{auth,clientIdSecretId}', "auth_client_secret_secret_id" = "config"#>>'{auth,clientSecretSecretId}'WHERE "config" IS NOT NULL AND ( ( "config"#>>'{auth,kind}' = 'header' AND "config"#>>'{auth,secretId}' IS NOT NULL ) OR ( "config"#>>'{auth,kind}' = 'oauth2' AND "config"#>>'{auth,connectionId}' IS NOT NULL ) );--> statement-breakpoint INSERT INTO "mcp_source_header" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']', s."id", h.key, CASE WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL ENDFROM "mcp_source" s, jsonb_each(s."config"->'headers') hWHERE s."config"->'headers' IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint INSERT INTO "mcp_source_query_param" ("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")SELECT s."scope_id", '[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']', s."id", q.key, CASE WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret' ELSE 'text' END, CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END, CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL ENDFROM "mcp_source" s, jsonb_each(s."config"->'queryParams') qWHERE s."config"->'queryParams' IS NOT NULLON CONFLICT DO NOTHING;--> statement-breakpoint -- Strip already-copied fields from config JSON. headers/queryParams-- are always safe; auth is only stripped on rows whose auth was the-- current shape (legacy inline-OAuth rows keep config.auth so-- migrateLegacyConnections can mint a Connection from it).UPDATE "mcp_source"SET "config" = "config" - 'headers' - 'queryParams'WHERE "config" IS NOT NULL;--> statement-breakpoint UPDATE "mcp_source"SET "config" = "config" - 'auth'WHERE "config" IS NOT NULL AND ( "config"#>>'{auth,kind}' = 'none' OR ( "config"#>>'{auth,kind}' = 'header' AND "config"#>>'{auth,secretId}' IS NOT NULL ) OR ( "config"#>>'{auth,kind}' = 'oauth2' AND "config"#>>'{auth,connectionId}' IS NOT NULL ) );
-- Normalize all plugin secret/connection refs out of JSON columns
-- into proper relational shape: graphql, openapi, mcp.
-- pg port of apps/local/drizzle/0007_normalize_plugin_secret_refs.sql.
-- (google-discovery is local-only — not in cloud's plugin list.)
-- ============================================================
-- graphql
-- ============================================================
-- Normalize graphql plugin: move secret/connection refs out of JSON
-- columns into proper relational shape so usagesForSecret /
-- usagesForConnection are one indexed SELECT instead of a JSON scan.
-- pg port of apps/local/drizzle/0007_normalize_graphql.sql.
CREATE TABLE "graphql_source_header" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "graphql_source_header_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "graphql_source_header_scope_id_idx" ON "graphql_source_header" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "graphql_source_header_source_id_idx" ON "graphql_source_header" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "graphql_source_header_secret_id_idx" ON "graphql_source_header" USING btree ("secret_id");--> statement-breakpoint
CREATE TABLE "graphql_source_query_param" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "graphql_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "graphql_source_query_param_scope_id_idx" ON "graphql_source_query_param" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "graphql_source_query_param_source_id_idx" ON "graphql_source_query_param" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "graphql_source_query_param_secret_id_idx" ON "graphql_source_query_param" USING btree ("secret_id");--> statement-breakpoint
-- New auth columns. `auth_kind` defaults to "none" so existing rows that
-- predate this migration are valid even if the json was null.
ALTER TABLE "graphql_source" ADD COLUMN "auth_kind" text DEFAULT 'none' NOT NULL;--> statement-breakpoint
ALTER TABLE "graphql_source" ADD COLUMN "auth_connection_id" text;--> statement-breakpoint
CREATE INDEX "graphql_source_auth_connection_id_idx" ON "graphql_source" USING btree ("auth_connection_id");--> statement-breakpoint
-- Backfill auth from the JSON column. Missing keys yield NULL, so a row
-- with auth=NULL or kind="none" leaves auth_connection_id NULL and
-- auth_kind defaulted to "none".
UPDATE "graphql_source"
SET
"auth_kind" = COALESCE("auth"->>'kind', 'none'),
"auth_connection_id" = "auth"->>'connectionId'
WHERE "auth" IS NOT NULL;--> statement-breakpoint
-- Backfill headers. For each (source, header_name) pair: if the value
-- is a json object with .secretId, write a kind=secret row; otherwise
-- write a kind=text row with the literal string. jsonb_each iterates
-- the keys of the headers object.
INSERT INTO "graphql_source_header"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']',
s."id",
h.key,
CASE
WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL END
FROM "graphql_source" s, jsonb_each(s."headers") h
WHERE s."headers" IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
-- Same for query_params.
INSERT INTO "graphql_source_query_param"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']',
s."id",
q.key,
CASE
WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL END
FROM "graphql_source" s, jsonb_each(s."query_params") q
WHERE s."query_params" IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
ALTER TABLE "graphql_source" DROP COLUMN "headers";--> statement-breakpoint
ALTER TABLE "graphql_source" DROP COLUMN "query_params";--> statement-breakpoint
ALTER TABLE "graphql_source" DROP COLUMN "auth";
--> statement-breakpoint
-- ============================================================
-- openapi
-- ============================================================
-- Normalize openapi plugin: move every direct secret/connection ref out
-- of JSON columns into proper relational shape. pg port of
-- apps/local/drizzle/0008_normalize_openapi.sql.
CREATE TABLE "openapi_source_query_param" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "openapi_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "openapi_source_query_param_scope_id_idx" ON "openapi_source_query_param" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "openapi_source_query_param_source_id_idx" ON "openapi_source_query_param" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "openapi_source_query_param_secret_id_idx" ON "openapi_source_query_param" USING btree ("secret_id");--> statement-breakpoint
CREATE TABLE "openapi_source_spec_fetch_header" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "openapi_source_spec_fetch_header_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_header_scope_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_header_source_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_header_secret_id_idx" ON "openapi_source_spec_fetch_header" USING btree ("secret_id");--> statement-breakpoint
CREATE TABLE "openapi_source_spec_fetch_query_param" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "openapi_source_spec_fetch_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_query_param_scope_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_query_param_source_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "openapi_source_spec_fetch_query_param_secret_id_idx" ON "openapi_source_spec_fetch_query_param" USING btree ("secret_id");--> statement-breakpoint
-- New columns on openapi_source_binding to flatten the value json.
-- `kind` defaults to 'text' so the ALTER works on existing rows; the
-- backfill below stamps the real value.
ALTER TABLE "openapi_source_binding" ADD COLUMN "kind" text DEFAULT 'text' NOT NULL;--> statement-breakpoint
ALTER TABLE "openapi_source_binding" ADD COLUMN "secret_id" text;--> statement-breakpoint
ALTER TABLE "openapi_source_binding" ADD COLUMN "connection_id" text;--> statement-breakpoint
ALTER TABLE "openapi_source_binding" ADD COLUMN "text_value" text;--> statement-breakpoint
CREATE INDEX "openapi_source_binding_secret_id_idx" ON "openapi_source_binding" USING btree ("secret_id");--> statement-breakpoint
CREATE INDEX "openapi_source_binding_connection_id_idx" ON "openapi_source_binding" USING btree ("connection_id");--> statement-breakpoint
UPDATE "openapi_source_binding"
SET
"kind" = COALESCE("value"->>'kind', 'text'),
"secret_id" = CASE WHEN "value"->>'kind' = 'secret' THEN "value"->>'secretId' ELSE NULL END,
"connection_id" = CASE WHEN "value"->>'kind' = 'connection' THEN "value"->>'connectionId' ELSE NULL END,
"text_value" = CASE WHEN "value"->>'kind' = 'text' THEN "value"->>'text' ELSE NULL END
WHERE "value" IS NOT NULL;--> statement-breakpoint
INSERT INTO "openapi_source_query_param"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']',
s."id",
q.key,
CASE
WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL END
FROM "openapi_source" s, jsonb_each(s."query_params") q
WHERE s."query_params" IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
INSERT INTO "openapi_source_spec_fetch_header"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']',
s."id",
h.key,
CASE
WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL END
FROM "openapi_source" s, jsonb_each(s."invocation_config"->'specFetchCredentials'->'headers') h
WHERE s."invocation_config"->'specFetchCredentials'->'headers' IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
INSERT INTO "openapi_source_spec_fetch_query_param"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']',
s."id",
q.key,
CASE
WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL END
FROM "openapi_source" s, jsonb_each(s."invocation_config"->'specFetchCredentials'->'queryParams') q
WHERE s."invocation_config"->'specFetchCredentials'->'queryParams' IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
-- Preserve any legacy OAuth payload from invocation_config.oauth2 into
-- the still-existing oauth2 column before we drop invocation_config.
-- migrateLegacyConnections runs after drizzle migrations and reads
-- oauth2 to detect the legacy shape; without this, rows that only had
-- their OAuth payload under invocation_config.oauth2 would lose it.
UPDATE "openapi_source"
SET "oauth2" = "invocation_config"->'oauth2'
WHERE "oauth2" IS NULL
AND "invocation_config"->'oauth2' IS NOT NULL;--> statement-breakpoint
ALTER TABLE "openapi_source_binding" DROP COLUMN "value";--> statement-breakpoint
ALTER TABLE "openapi_source" DROP COLUMN "query_params";--> statement-breakpoint
ALTER TABLE "openapi_source" DROP COLUMN "invocation_config";
--> statement-breakpoint
-- ============================================================
-- mcp
-- ============================================================
-- Normalize mcp plugin: lift the McpConnectionAuth secret/connection
-- refs and the SecretBackedMap headers/query_params out of
-- mcp_source.config JSON into proper columns / child tables. pg port
-- of apps/local/drizzle/0009_normalize_mcp.sql.
CREATE TABLE "mcp_source_header" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "mcp_source_header_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "mcp_source_header_scope_id_idx" ON "mcp_source_header" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "mcp_source_header_source_id_idx" ON "mcp_source_header" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "mcp_source_header_secret_id_idx" ON "mcp_source_header" USING btree ("secret_id");--> statement-breakpoint
CREATE TABLE "mcp_source_query_param" (
"id" text NOT NULL,
"scope_id" text NOT NULL,
"source_id" text NOT NULL,
"name" text NOT NULL,
"kind" text NOT NULL,
"text_value" text,
"secret_id" text,
"secret_prefix" text,
CONSTRAINT "mcp_source_query_param_scope_id_id_pk" PRIMARY KEY("scope_id","id")
);
--> statement-breakpoint
CREATE INDEX "mcp_source_query_param_scope_id_idx" ON "mcp_source_query_param" USING btree ("scope_id");--> statement-breakpoint
CREATE INDEX "mcp_source_query_param_source_id_idx" ON "mcp_source_query_param" USING btree ("source_id");--> statement-breakpoint
CREATE INDEX "mcp_source_query_param_secret_id_idx" ON "mcp_source_query_param" USING btree ("secret_id");--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_kind" text DEFAULT 'none' NOT NULL;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_header_name" text;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_secret_id" text;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_secret_prefix" text;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_connection_id" text;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_client_id_secret_id" text;--> statement-breakpoint
ALTER TABLE "mcp_source" ADD COLUMN "auth_client_secret_secret_id" text;--> statement-breakpoint
CREATE INDEX "mcp_source_auth_secret_id_idx" ON "mcp_source" USING btree ("auth_secret_id");--> statement-breakpoint
CREATE INDEX "mcp_source_auth_connection_id_idx" ON "mcp_source" USING btree ("auth_connection_id");--> statement-breakpoint
CREATE INDEX "mcp_source_auth_client_id_secret_id_idx" ON "mcp_source" USING btree ("auth_client_id_secret_id");--> statement-breakpoint
CREATE INDEX "mcp_source_auth_client_secret_secret_id_idx" ON "mcp_source" USING btree ("auth_client_secret_secret_id");--> statement-breakpoint
-- Only update rows with explicitly current-shape auth (kind=header w/
-- secretId, or kind=oauth2 w/ connectionId). Legacy inline-OAuth rows
-- are left untouched so the post-migrate migrateLegacyConnections
-- script can convert them to a Connection.
UPDATE "mcp_source"
SET
"auth_kind" = "config"#>>'{auth,kind}',
"auth_header_name" = "config"#>>'{auth,headerName}',
"auth_secret_id" = "config"#>>'{auth,secretId}',
"auth_secret_prefix" = "config"#>>'{auth,prefix}',
"auth_connection_id" = "config"#>>'{auth,connectionId}',
"auth_client_id_secret_id" = "config"#>>'{auth,clientIdSecretId}',
"auth_client_secret_secret_id" = "config"#>>'{auth,clientSecretSecretId}'
WHERE "config" IS NOT NULL
AND (
(
"config"#>>'{auth,kind}' = 'header'
AND "config"#>>'{auth,secretId}' IS NOT NULL
)
OR (
"config"#>>'{auth,kind}' = 'oauth2'
AND "config"#>>'{auth,connectionId}' IS NOT NULL
)
);--> statement-breakpoint
INSERT INTO "mcp_source_header"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(h.key)::text || ']',
s."id",
h.key,
CASE
WHEN jsonb_typeof(h.value) = 'object' AND h.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(h.value) = 'string' THEN h.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(h.value) = 'object' THEN h.value->>'prefix' ELSE NULL END
FROM "mcp_source" s, jsonb_each(s."config"->'headers') h
WHERE s."config"->'headers' IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
INSERT INTO "mcp_source_query_param"
("scope_id", "id", "source_id", "name", "kind", "text_value", "secret_id", "secret_prefix")
SELECT
s."scope_id",
'[' || to_jsonb(s."id")::text || ',' || to_jsonb(q.key)::text || ']',
s."id",
q.key,
CASE
WHEN jsonb_typeof(q.value) = 'object' AND q.value ? 'secretId' THEN 'secret'
ELSE 'text'
END,
CASE WHEN jsonb_typeof(q.value) = 'string' THEN q.value #>> '{}' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'secretId' ELSE NULL END,
CASE WHEN jsonb_typeof(q.value) = 'object' THEN q.value->>'prefix' ELSE NULL END
FROM "mcp_source" s, jsonb_each(s."config"->'queryParams') q
WHERE s."config"->'queryParams' IS NOT NULL
ON CONFLICT DO NOTHING;--> statement-breakpoint
-- Strip already-copied fields from config JSON. headers/queryParams
-- are always safe; auth is only stripped on rows whose auth was the
-- current shape (legacy inline-OAuth rows keep config.auth so
-- migrateLegacyConnections can mint a Connection from it).
UPDATE "mcp_source"
SET "config" = "config" - 'headers' - 'queryParams'
WHERE "config" IS NOT NULL;--> statement-breakpoint
UPDATE "mcp_source"
SET "config" = "config" - 'auth'
WHERE "config" IS NOT NULL
AND (
"config"#>>'{auth,kind}' = 'none'
OR (
"config"#>>'{auth,kind}' = 'header'
AND "config"#>>'{auth,secretId}' IS NOT NULL
)
OR (
"config"#>>'{auth,kind}' = 'oauth2'
AND "config"#>>'{auth,connectionId}' IS NOT NULL
)
);