feat(ui): structured editors for script schemas and alarm triggers
Replace raw-JSON text inputs with rich UI: script parameter/return types use a JSON Schema builder (SchemaBuilder + JsonSchemaShapeParser, with a migration to convert existing definitions); alarm trigger config uses a type-aware editor with a flattened attribute picker (AlarmTriggerEditor). AlarmActor gains optional direction (rising/falling/either) on RateOfChange triggers.
This commit is contained in:
1300
src/ScadaLink.ConfigurationDatabase/Migrations/20260512211204_MigrateParametersToJsonSchema.Designer.cs
generated
Normal file
1300
src/ScadaLink.ConfigurationDatabase/Migrations/20260512211204_MigrateParametersToJsonSchema.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MigrateParametersToJsonSchema : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Convert legacy flat-shape parameter / return JSON in TemplateScripts,
|
||||
// SharedScripts, and ApiMethods to JSON Schema.
|
||||
//
|
||||
// Parameters [{name,type,required,itemType?}]
|
||||
// → {"type":"object","properties":{<name>:{"type":<jsType>}},"required":[...]}
|
||||
//
|
||||
// Return {type,itemType?}
|
||||
// → {"type":<jsType>} or {"type":"array","items":{"type":<inner>}}
|
||||
//
|
||||
// Idempotent: only rows whose value starts with '[' (parameters) or that
|
||||
// contain the legacy 'List' sentinel (return) are touched. Already-converted
|
||||
// rows are skipped.
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
IF OBJECT_ID('dbo.fn_LegacyTypeToJsonSchemaType', 'FN') IS NOT NULL
|
||||
DROP FUNCTION dbo.fn_LegacyTypeToJsonSchemaType;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE FUNCTION dbo.fn_LegacyTypeToJsonSchemaType(@legacy NVARCHAR(50))
|
||||
RETURNS NVARCHAR(50)
|
||||
AS
|
||||
BEGIN
|
||||
RETURN
|
||||
CASE LOWER(ISNULL(@legacy, 'string'))
|
||||
WHEN 'boolean' THEN 'boolean'
|
||||
WHEN 'bool' THEN 'boolean'
|
||||
WHEN 'integer' THEN 'integer'
|
||||
WHEN 'int' THEN 'integer'
|
||||
WHEN 'int32' THEN 'integer'
|
||||
WHEN 'int64' THEN 'integer'
|
||||
WHEN 'float' THEN 'number'
|
||||
WHEN 'double' THEN 'number'
|
||||
WHEN 'decimal' THEN 'number'
|
||||
WHEN 'number' THEN 'number'
|
||||
WHEN 'string' THEN 'string'
|
||||
WHEN 'datetime' THEN 'string'
|
||||
WHEN 'object' THEN 'object'
|
||||
WHEN 'list' THEN 'array'
|
||||
WHEN 'array' THEN 'array'
|
||||
ELSE 'string'
|
||||
END;
|
||||
END;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
IF OBJECT_ID('dbo.fn_LegacyParametersToJsonSchema', 'FN') IS NOT NULL
|
||||
DROP FUNCTION dbo.fn_LegacyParametersToJsonSchema;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE FUNCTION dbo.fn_LegacyParametersToJsonSchema(@legacy NVARCHAR(MAX))
|
||||
RETURNS NVARCHAR(MAX)
|
||||
AS
|
||||
BEGIN
|
||||
IF @legacy IS NULL OR LTRIM(@legacy) = '' RETURN NULL;
|
||||
IF LEFT(LTRIM(@legacy), 1) <> '[' RETURN @legacy; -- already schema-shaped
|
||||
|
||||
DECLARE @props NVARCHAR(MAX) = (
|
||||
SELECT STRING_AGG(
|
||||
CONCAT(
|
||||
'""',
|
||||
STRING_ESCAPE(JSON_VALUE(p.value, '$.name'), 'json'),
|
||||
'"":',
|
||||
CASE
|
||||
WHEN LOWER(ISNULL(JSON_VALUE(p.value, '$.type'), 'string')) IN ('list', 'array')
|
||||
THEN CONCAT(
|
||||
'{""type"":""array"",""items"":{""type"":""',
|
||||
dbo.fn_LegacyTypeToJsonSchemaType(JSON_VALUE(p.value, '$.itemType')),
|
||||
'""}}')
|
||||
ELSE CONCAT(
|
||||
'{""type"":""',
|
||||
dbo.fn_LegacyTypeToJsonSchemaType(JSON_VALUE(p.value, '$.type')),
|
||||
'""}')
|
||||
END),
|
||||
',')
|
||||
WITHIN GROUP (ORDER BY p.[key])
|
||||
FROM OPENJSON(@legacy) p
|
||||
WHERE JSON_VALUE(p.value, '$.name') IS NOT NULL
|
||||
AND JSON_VALUE(p.value, '$.name') <> ''
|
||||
);
|
||||
|
||||
DECLARE @required NVARCHAR(MAX) = (
|
||||
SELECT STRING_AGG(
|
||||
CONCAT('""', STRING_ESCAPE(JSON_VALUE(p.value, '$.name'), 'json'), '""'),
|
||||
',')
|
||||
WITHIN GROUP (ORDER BY p.[key])
|
||||
FROM OPENJSON(@legacy) p
|
||||
WHERE JSON_VALUE(p.value, '$.name') IS NOT NULL
|
||||
AND JSON_VALUE(p.value, '$.name') <> ''
|
||||
AND LOWER(ISNULL(JSON_VALUE(p.value, '$.required'), 'true')) <> 'false'
|
||||
);
|
||||
|
||||
RETURN
|
||||
'{""type"":""object"",""properties"":{' + ISNULL(@props, '') + '}'
|
||||
+ CASE WHEN @required IS NULL OR @required = '' THEN ''
|
||||
ELSE ',""required"":[' + @required + ']'
|
||||
END
|
||||
+ '}';
|
||||
END;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
IF OBJECT_ID('dbo.fn_LegacyReturnToJsonSchema', 'FN') IS NOT NULL
|
||||
DROP FUNCTION dbo.fn_LegacyReturnToJsonSchema;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE FUNCTION dbo.fn_LegacyReturnToJsonSchema(@legacy NVARCHAR(MAX))
|
||||
RETURNS NVARCHAR(MAX)
|
||||
AS
|
||||
BEGIN
|
||||
IF @legacy IS NULL OR LTRIM(@legacy) = '' RETURN NULL;
|
||||
IF LEFT(LTRIM(@legacy), 1) <> '{' RETURN @legacy;
|
||||
|
||||
DECLARE @legacyType NVARCHAR(50) = JSON_VALUE(@legacy, '$.type');
|
||||
IF @legacyType IS NULL RETURN @legacy;
|
||||
|
||||
-- Already JSON Schema (lowercase types, no itemType legacy sentinel): leave it.
|
||||
IF @legacyType IN ('boolean','integer','number','string','object','array')
|
||||
AND JSON_VALUE(@legacy, '$.itemType') IS NULL
|
||||
RETURN @legacy;
|
||||
|
||||
IF LOWER(@legacyType) = 'list'
|
||||
BEGIN
|
||||
DECLARE @inner NVARCHAR(50) =
|
||||
dbo.fn_LegacyTypeToJsonSchemaType(JSON_VALUE(@legacy, '$.itemType'));
|
||||
RETURN CONCAT('{""type"":""array"",""items"":{""type"":""', @inner, '""}}');
|
||||
END;
|
||||
|
||||
RETURN CONCAT('{""type"":""', dbo.fn_LegacyTypeToJsonSchemaType(@legacyType), '""}');
|
||||
END;
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE TemplateScripts
|
||||
SET ParameterDefinitions = dbo.fn_LegacyParametersToJsonSchema(ParameterDefinitions)
|
||||
WHERE ParameterDefinitions IS NOT NULL
|
||||
AND LEFT(LTRIM(ParameterDefinitions), 1) = '[';
|
||||
|
||||
UPDATE TemplateScripts
|
||||
SET ReturnDefinition = dbo.fn_LegacyReturnToJsonSchema(ReturnDefinition)
|
||||
WHERE ReturnDefinition IS NOT NULL
|
||||
AND LEFT(LTRIM(ReturnDefinition), 1) = '{';
|
||||
|
||||
UPDATE SharedScripts
|
||||
SET ParameterDefinitions = dbo.fn_LegacyParametersToJsonSchema(ParameterDefinitions)
|
||||
WHERE ParameterDefinitions IS NOT NULL
|
||||
AND LEFT(LTRIM(ParameterDefinitions), 1) = '[';
|
||||
|
||||
UPDATE SharedScripts
|
||||
SET ReturnDefinition = dbo.fn_LegacyReturnToJsonSchema(ReturnDefinition)
|
||||
WHERE ReturnDefinition IS NOT NULL
|
||||
AND LEFT(LTRIM(ReturnDefinition), 1) = '{';
|
||||
|
||||
UPDATE ApiMethods
|
||||
SET ParameterDefinitions = dbo.fn_LegacyParametersToJsonSchema(ParameterDefinitions)
|
||||
WHERE ParameterDefinitions IS NOT NULL
|
||||
AND LEFT(LTRIM(ParameterDefinitions), 1) = '[';
|
||||
|
||||
UPDATE ApiMethods
|
||||
SET ReturnDefinition = dbo.fn_LegacyReturnToJsonSchema(ReturnDefinition)
|
||||
WHERE ReturnDefinition IS NOT NULL
|
||||
AND LEFT(LTRIM(ReturnDefinition), 1) = '{';
|
||||
");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
DROP FUNCTION IF EXISTS dbo.fn_LegacyParametersToJsonSchema;
|
||||
DROP FUNCTION IF EXISTS dbo.fn_LegacyReturnToJsonSchema;
|
||||
DROP FUNCTION IF EXISTS dbo.fn_LegacyTypeToJsonSchemaType;
|
||||
");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Lossy: JSON Schema can express fields (descriptions, defaults, enums,
|
||||
// nested objects) that the legacy flat shape cannot represent. Reverse
|
||||
// migration is not supported.
|
||||
throw new System.NotSupportedException(
|
||||
"Reverse migration from JSON Schema to legacy flat shape is not supported because the conversion is lossy.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user