142 lines
4.5 KiB
Go
142 lines
4.5 KiB
Go
|
|
// Package database define database operation functions
|
||
|
|
package database
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"modelRT/constants"
|
||
|
|
"modelRT/model"
|
||
|
|
"modelRT/sql"
|
||
|
|
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
type parameterInitializationRoute struct {
|
||
|
|
TableName string `gorm:"column:name"`
|
||
|
|
ModelName string `gorm:"column:tag"`
|
||
|
|
AttributeGroup string `gorm:"column:group_name"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// QueryParameterInitializationRecords loads every parameter accepted by the
|
||
|
|
// data-object query API. Dynamic parameters are resolved through
|
||
|
|
// project_manager; component parameters are read directly from component.
|
||
|
|
func QueryParameterInitializationRecords(ctx context.Context, db *gorm.DB) ([]model.ParameterInitializationRecord, error) {
|
||
|
|
if db == nil {
|
||
|
|
return nil, fmt.Errorf("postgres client is nil")
|
||
|
|
}
|
||
|
|
|
||
|
|
var routes []parameterInitializationRoute
|
||
|
|
if err := db.WithContext(ctx).
|
||
|
|
Raw(
|
||
|
|
compactParameterSQL(sql.ParameterInitializationRoutes),
|
||
|
|
constants.SupportedDynamicParameterAttributeGroups(),
|
||
|
|
).
|
||
|
|
Scan(&routes).Error; err != nil {
|
||
|
|
return nil, fmt.Errorf("query parameter initialization routes: %w", err)
|
||
|
|
}
|
||
|
|
if err := validateParameterInitializationRoutes(routes); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
records := make([]model.ParameterInitializationRecord, 0)
|
||
|
|
for _, route := range routes {
|
||
|
|
quotedTableName := `"` + route.TableName + `"`
|
||
|
|
query := compactParameterSQL(
|
||
|
|
fmt.Sprintf(sql.DynamicParameterInitializationRows, quotedTableName),
|
||
|
|
)
|
||
|
|
|
||
|
|
var tableRecords []model.ParameterInitializationRecord
|
||
|
|
if err := db.WithContext(ctx).
|
||
|
|
Raw(query, route.TableName, route.ModelName, route.AttributeGroup).
|
||
|
|
Scan(&tableRecords).Error; err != nil {
|
||
|
|
return nil, fmt.Errorf(
|
||
|
|
"query parameter initialization table %q for model %q group %q: %w",
|
||
|
|
route.TableName,
|
||
|
|
route.ModelName,
|
||
|
|
route.AttributeGroup,
|
||
|
|
err,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
if err := validateParameterInitializationRecords(tableRecords); err != nil {
|
||
|
|
return nil, fmt.Errorf("validate parameter initialization table %q: %w", route.TableName, err)
|
||
|
|
}
|
||
|
|
records = append(records, tableRecords...)
|
||
|
|
}
|
||
|
|
|
||
|
|
var componentRecords []model.ParameterInitializationRecord
|
||
|
|
if err := db.WithContext(ctx).
|
||
|
|
Raw(compactParameterSQL(sql.ComponentParameterInitializationRows)).
|
||
|
|
Scan(&componentRecords).Error; err != nil {
|
||
|
|
return nil, fmt.Errorf("query component parameter initialization records: %w", err)
|
||
|
|
}
|
||
|
|
if err := validateParameterInitializationRecords(componentRecords); err != nil {
|
||
|
|
return nil, fmt.Errorf("validate component parameter initialization records: %w", err)
|
||
|
|
}
|
||
|
|
records = append(records, componentRecords...)
|
||
|
|
return records, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateParameterInitializationRoutes(routes []parameterInitializationRoute) error {
|
||
|
|
seen := make(map[string]struct{}, len(routes))
|
||
|
|
for _, route := range routes {
|
||
|
|
if !validParameterTableName(route.TableName) {
|
||
|
|
return fmt.Errorf("project_manager contains unsupported parameter table name %q", route.TableName)
|
||
|
|
}
|
||
|
|
if !constants.IsSupportedParameterAttributeGroup(route.AttributeGroup) ||
|
||
|
|
route.AttributeGroup == constants.ComponentParameterAttributeGroup {
|
||
|
|
return fmt.Errorf("project_manager contains unsupported dynamic attribute group %q", route.AttributeGroup)
|
||
|
|
}
|
||
|
|
|
||
|
|
key := route.ModelName + "\x00" + route.AttributeGroup
|
||
|
|
if _, exists := seen[key]; exists {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"model %q and attribute group %q match more than one project_manager record",
|
||
|
|
route.ModelName,
|
||
|
|
route.AttributeGroup,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
seen[key] = struct{}{}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateParameterInitializationRecords(records []model.ParameterInitializationRecord) error {
|
||
|
|
for _, record := range records {
|
||
|
|
switch record.DynamicRecordCount {
|
||
|
|
case 1:
|
||
|
|
case 0:
|
||
|
|
return fmt.Errorf(
|
||
|
|
"component %q has no %q parameter record",
|
||
|
|
record.ComponentTag,
|
||
|
|
record.AttributeGroup,
|
||
|
|
)
|
||
|
|
default:
|
||
|
|
return fmt.Errorf(
|
||
|
|
"component %q has %d %q parameter records",
|
||
|
|
record.ComponentTag,
|
||
|
|
record.DynamicRecordCount,
|
||
|
|
record.AttributeGroup,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
if record.AttributeName == "" || record.AttributeType == "" {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"component %q group %q contains an invalid parameter column",
|
||
|
|
record.ComponentTag,
|
||
|
|
record.AttributeGroup,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
switch record.DescriptionCount {
|
||
|
|
case 0:
|
||
|
|
return fmt.Errorf("parameter description not found for attribute %q", record.AttributeName)
|
||
|
|
case 1:
|
||
|
|
if !record.Description.Valid {
|
||
|
|
return fmt.Errorf("parameter description is null for attribute %q", record.AttributeName)
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return fmt.Errorf("ambiguous parameter description for attribute %q", record.AttributeName)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|