registry/internal/database/migrations/006_migrate_server_json_camelcase.sql
Preeti Dewani 53f808a2c1
add migraiton file to convert snake case to camel case (#522)
## Motivation and Context
- Make all entries in current database matching the new schema

## How Has This Been Tested?
- locally

## Breaking Changes
- Yes

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [X] Breaking change (fix or feature that would cause existing
functionality to change)
- [X] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [X] My code follows the repository's style guidelines
- [X] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [X] I have added or updated documentation as needed

Before migration:

```
{
      "name": "io.github.kenjihikmatullah/productboard-mcp",
      "description": "Integrate the Productboard API into agentic workflows via MCP",
      "status": "active",
      "repository": {
        "url": "https://github.com/kenjihikmatullah/productboard-mcp",
        "source": "github",
        "id": "939104710"
      },
      "version": "0.0.1-seed",
      "packages": [
        {
          "registry_type": "npm",
          "identifier": "productboard-mcp",
          "version": "1.0.1",
          "transport": {
            "type": "stdio"
          }
        }
      ],
      "_meta": {
        "io.modelcontextprotocol.registry/official": {
          "id": "00613acb-73e2-4f93-8b96-296df17316c8",
          "published_at": "0001-01-01T00:00:00Z",
          "updated_at": "0001-01-01T00:00:00Z",
          "is_latest": true
        }
      }
    }
    
```

After migration:

```
 {
      "name": "io.github.kenjihikmatullah/productboard-mcp",
      "description": "Integrate the Productboard API into agentic workflows via MCP",
      "status": "active",
      "repository": {
        "url": "https://github.com/kenjihikmatullah/productboard-mcp",
        "source": "github",
        "id": "939104710"
      },
      "version": "0.0.1-seed",
      "packages": [
        {
          "registryType": "npm",
          "identifier": "productboard-mcp",
          "version": "1.0.1",
          "transport": {
            "type": "stdio"
          },
        }
      ],
      "_meta": {
        "io.modelcontextprotocol.registry/official": {
          "serverId": "83cfe55c-55c5-4e7e-b9c0-6d7d369f52ed",
          "versionId": "00613acb-73e2-4f93-8b96-296df17316c8",
          "publishedAt": "0001-01-01T00:00:00Z",
          "updatedAt": "0001-01-01T00:00:00Z",
          "isLatest": true
        }
      }
    }
2025-09-21 15:26:15 +03:00

82 lines
3.1 KiB
PL/PgSQL

-- Migrate server.json fields from snake_case to camelCase
-- This migration converts all fields including those in _meta objects
-- Helper function to recursively convert snake_case keys to camelCase in JSON objects
-- This handles nested structures like arguments, environment variables, etc.
CREATE OR REPLACE FUNCTION convert_object_keys_to_camelcase(input_json jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb := '{}';
key text;
value jsonb;
new_key text;
BEGIN
-- Handle null input
IF input_json IS NULL THEN
RETURN NULL;
END IF;
-- Iterate through all keys in the object
FOR key, value IN SELECT * FROM jsonb_each(input_json)
LOOP
-- Convert snake_case keys to camelCase
new_key := CASE
WHEN key = 'registry_type' THEN 'registryType'
WHEN key = 'registry_base_url' THEN 'registryBaseUrl'
WHEN key = 'file_sha256' THEN 'fileSha256'
WHEN key = 'runtime_hint' THEN 'runtimeHint'
WHEN key = 'runtime_arguments' THEN 'runtimeArguments'
WHEN key = 'package_arguments' THEN 'packageArguments'
WHEN key = 'environment_variables' THEN 'environmentVariables'
WHEN key = 'is_required' THEN 'isRequired'
WHEN key = 'is_secret' THEN 'isSecret'
WHEN key = 'value_hint' THEN 'valueHint'
WHEN key = 'is_repeated' THEN 'isRepeated'
WHEN key = 'website_url' THEN 'websiteUrl'
-- _meta specific fields
WHEN key = 'is_latest' THEN 'isLatest'
WHEN key = 'published_at' THEN 'publishedAt'
WHEN key = 'updated_at' THEN 'updatedAt'
ELSE key -- Keep other keys unchanged
END;
-- Process values based on their type
IF jsonb_typeof(value) = 'array' THEN
-- Process array elements
result := jsonb_set(result, ARRAY[new_key],
(SELECT jsonb_agg(
CASE
WHEN jsonb_typeof(elem) = 'object' THEN convert_object_keys_to_camelcase(elem)
ELSE elem
END
) FROM jsonb_array_elements(value) AS elem)
);
ELSIF jsonb_typeof(value) = 'object' THEN
-- Process nested objects
result := jsonb_set(result, ARRAY[new_key], convert_object_keys_to_camelcase(value));
ELSE
-- Keep all primitive values (strings, numbers, booleans, null) unchanged
result := jsonb_set(result, ARRAY[new_key], value);
END IF;
END LOOP;
RETURN result;
END;
$$;
-- Update all server records to use camelCase field names
-- This converts snake_case keys in all fields including _meta sections
UPDATE servers
SET value = convert_object_keys_to_camelcase(value)
WHERE value IS NOT NULL;
-- Update $schema field to latest version if it exists
UPDATE servers
SET value = jsonb_set(value, '{$schema}', '"https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json"')
WHERE value ? '$schema' AND value IS NOT NULL;
-- Clean up the helper function
DROP FUNCTION IF EXISTS convert_object_keys_to_camelcase(jsonb);