package integration import ( "context" "encoding/json" integrationbiz "kra/internal/biz/integration" "kra/internal/config" "kra/internal/service/dto" ) type IntegrationConfigService struct { uc *integrationbiz.IntegrationConfigUsecase } func NewIntegrationConfigService(uc *integrationbiz.IntegrationConfigUsecase) *IntegrationConfigService { return &IntegrationConfigService{uc: uc} } func (s *IntegrationConfigService) List(ctx context.Context, kind string) ([]*dto.IntegrationConfigResponse, error) { configs, err := s.uc.List(ctx, kind) if err != nil { return nil, err } byProvider := make(map[string]*integrationbiz.IntegrationConfig, len(configs)) for _, config := range configs { if config != nil { byProvider[config.Provider] = config } } definitions := integrationbiz.IntegrationDefinitions(kind) result := make([]*dto.IntegrationConfigResponse, 0, len(definitions)+len(configs)) for _, definition := range definitions { config, configured := byProvider[definition.Provider] values := mergeConfigJSON(definition.Defaults, nil) enabled := false if configured { values = mergeConfigJSON(definition.Defaults, config.Values) enabled = config.Enabled delete(byProvider, definition.Provider) } result = append(result, &dto.IntegrationConfigResponse{Kind: definition.Kind, Provider: definition.Provider, Name: definition.Name, Description: definition.Description, Enabled: enabled, Configured: configured, Config: redactIntegrationJSON(values), Fields: integrationFieldsDTO(definition.Fields)}) } for _, config := range configs { if _, exists := byProvider[config.Provider]; !exists { continue } result = append(result, &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: redactIntegrationJSON(config.Values)}) } return result, nil } func (s *IntegrationConfigService) Find(ctx context.Context, kind, provider string) (*dto.IntegrationConfigResponse, error) { config, err := s.uc.Find(ctx, kind, provider) if err != nil { return nil, err } definition, found := integrationbiz.IntegrationDefinition(config.Kind, config.Provider) response := &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: redactIntegrationJSON(config.Values)} if found { response.Name = definition.Name response.Description = definition.Description response.Fields = integrationFieldsDTO(definition.Fields) response.Config = redactIntegrationJSON(mergeConfigJSON(definition.Defaults, config.Values)) } return response, nil } func redactIntegrationJSON(raw json.RawMessage) json.RawMessage { var value any if json.Unmarshal(raw, &value) != nil { return raw } var walk func(any) walk = func(v any) { switch x := v.(type) { case map[string]any: for k, item := range x { if integrationbiz.IsIntegrationSecretKey(k) { if text, ok := item.(string); ok && text != "" { x[k] = config.MaskedSecret } } else { walk(item) } } case []any: for _, item := range x { walk(item) } } } walk(value) out, _ := json.Marshal(value) return out } func (s *IntegrationConfigService) Save(ctx context.Context, kind, provider string, req *dto.IntegrationConfigRequest) error { if req == nil { return s.uc.Save(ctx, nil) } return s.uc.Save(ctx, &integrationbiz.IntegrationConfig{Kind: kind, Provider: provider, Enabled: req.Enabled, Values: req.Config}) } func (s *IntegrationConfigService) Test(ctx context.Context, kind, provider string, req *dto.IntegrationConfigRequest) error { if req == nil { return s.uc.Test(ctx, nil) } return s.uc.Test(ctx, &integrationbiz.IntegrationConfig{Kind: kind, Provider: provider, Enabled: true, Values: req.Config}) } func (s *IntegrationConfigService) Delete(ctx context.Context, kind, provider string) error { return s.uc.Delete(ctx, kind, provider) } func mergeConfigJSON(defaults map[string]any, raw json.RawMessage) json.RawMessage { stored := map[string]any{} _ = json.Unmarshal(raw, &stored) values := integrationbiz.MergeIntegrationDefaults(defaults, stored) encoded, _ := json.Marshal(values) return encoded } func integrationFieldsDTO(fields []integrationbiz.IntegrationConfigField) []dto.IntegrationConfigField { if fields == nil { return nil } out := make([]dto.IntegrationConfigField, 0, len(fields)) for _, field := range fields { item := dto.IntegrationConfigField{Key: field.Key, Label: field.Label, Type: field.Type, Required: field.Required, Secret: field.Secret, Placeholder: field.Placeholder, Description: field.Description} if field.Options != nil { item.Options = make([]dto.IntegrationConfigOption, 0, len(field.Options)) for _, option := range field.Options { item.Options = append(item.Options, dto.IntegrationConfigOption{Label: option.Label, Value: option.Value}) } } out = append(out, item) } return out }