浏览代码

fix(config): add api to export selected rules

Signed-off-by: Jianxiang Ran <rxan_embedded@163.com>
Jianxiang Ran 2 年之前
父节点
当前提交
de55cd6f8d

+ 13 - 0
internal/binder/function/binder.go

@@ -16,6 +16,7 @@ package function
 
 import (
 	"github.com/lf-edge/ekuiper/internal/binder"
+	"github.com/lf-edge/ekuiper/internal/plugin"
 	"github.com/lf-edge/ekuiper/pkg/api"
 	"github.com/lf-edge/ekuiper/pkg/ast"
 	"github.com/lf-edge/ekuiper/pkg/errorx"
@@ -63,6 +64,18 @@ func Function(name string) (api.Function, error) {
 	return nil, e.GetError()
 }
 
+func GetFunctionPlugin(name string) (plugin.EXTENSION_TYPE, string, string) {
+	for _, sf := range funcFactories {
+		t, s1, s2 := sf.GetFunctionPlugin(name)
+		if t == plugin.NONE_EXTENSION {
+			continue
+		} else {
+			return t, s1, s2
+		}
+	}
+	return plugin.NONE_EXTENSION, "", ""
+}
+
 func HasFunctionSet(name string) bool {
 	for _, sf := range funcFactories {
 		r := sf.HasFunctionSet(name)

+ 6 - 1
internal/binder/function/function.go

@@ -129,7 +129,12 @@ func (m *Manager) HasFunctionSet(name string) bool {
 }
 
 func (m *Manager) GetFunctionPlugin(funcName string) (plugin.EXTENSION_TYPE, string, string) {
-	return plugin.INTERNAL, "", ""
+	_, ok := builtins[funcName]
+	if !ok {
+		return plugin.NONE_EXTENSION, "", ""
+	} else {
+		return plugin.INTERNAL, "", ""
+	}
 }
 
 func (m *Manager) ConvName(n string) (string, bool) {

+ 25 - 0
internal/binder/io/binder.go

@@ -17,6 +17,7 @@ package io
 import (
 	"fmt"
 	"github.com/lf-edge/ekuiper/internal/binder"
+	"github.com/lf-edge/ekuiper/internal/plugin"
 	"github.com/lf-edge/ekuiper/pkg/api"
 	"github.com/lf-edge/ekuiper/pkg/errorx"
 )
@@ -68,6 +69,18 @@ func Source(name string) (api.Source, error) {
 	return nil, e.GetError()
 }
 
+func GetSourcePlugin(name string) (plugin.EXTENSION_TYPE, string, string) {
+	for _, sf := range sourceFactories {
+		t, s1, s2 := sf.GetSourcePlugin(name)
+		if t == plugin.NONE_EXTENSION {
+			continue
+		} else {
+			return t, s1, s2
+		}
+	}
+	return plugin.NONE_EXTENSION, "", ""
+}
+
 func Sink(name string) (api.Sink, error) {
 	e := make(errorx.MultiError)
 	for i, sf := range sinkFactories {
@@ -82,6 +95,18 @@ func Sink(name string) (api.Sink, error) {
 	return nil, e.GetError()
 }
 
+func GetSinkPlugin(name string) (plugin.EXTENSION_TYPE, string, string) {
+	for _, sf := range sinkFactories {
+		t, s1, s2 := sf.GetSinkPlugin(name)
+		if t == plugin.NONE_EXTENSION {
+			continue
+		} else {
+			return t, s1, s2
+		}
+	}
+	return plugin.NONE_EXTENSION, "", ""
+}
+
 func LookupSource(name string) (api.LookupSource, error) {
 	e := make(errorx.MultiError)
 	for i, sf := range sourceFactories {

+ 12 - 4
internal/binder/io/builtin.go

@@ -62,8 +62,12 @@ func (m *Manager) Source(name string) (api.Source, error) {
 	return nil, nil
 }
 
-func (m *Manager) GetSourcePlugin(_ string) (plugin2.EXTENSION_TYPE, string, string) {
-	return plugin2.INTERNAL, "", ""
+func (m *Manager) GetSourcePlugin(name string) (plugin2.EXTENSION_TYPE, string, string) {
+	if _, ok := sources[name]; ok {
+		return plugin2.INTERNAL, "", ""
+	} else {
+		return plugin2.NONE_EXTENSION, "", ""
+	}
 }
 
 func (m *Manager) LookupSource(name string) (api.LookupSource, error) {
@@ -80,8 +84,12 @@ func (m *Manager) Sink(name string) (api.Sink, error) {
 	return nil, nil
 }
 
-func (m *Manager) GetSinkPlugin(_ string) (plugin2.EXTENSION_TYPE, string, string) {
-	return plugin2.INTERNAL, "", ""
+func (m *Manager) GetSinkPlugin(name string) (plugin2.EXTENSION_TYPE, string, string) {
+	if _, ok := sinks[name]; ok {
+		return plugin2.INTERNAL, "", ""
+	} else {
+		return plugin2.NONE_EXTENSION, "", ""
+	}
 }
 
 var m = &Manager{}

+ 1 - 1
internal/binder/mock/mock_factory.go

@@ -77,7 +77,7 @@ func (f *MockFactory) HasFunctionSet(funcName string) bool {
 }
 
 func (f *MockFactory) GetFunctionPlugin(funcName string) (plugin.EXTENSION_TYPE, string, string) {
-	return plugin.INTERNAL, "", ""
+	return plugin.NONE_EXTENSION, "", ""
 }
 
 type mockFunc struct{}

+ 10 - 15
internal/meta/yamlConfigMeta.go

@@ -440,18 +440,16 @@ func GetConfigurations() YamlConfigurationSet {
 }
 
 type YamlConfigurationKeys struct {
-	sources     map[string][]string
-	sinks       map[string][]string
-	connections map[string][]string
+	Sources map[string][]string
+	Sinks   map[string][]string
 }
 
 func GetConfigurationsFor(yaml YamlConfigurationKeys) YamlConfigurationSet {
 	ConfigManager.lock.RLock()
 	defer ConfigManager.lock.RUnlock()
 
-	sourcesConfigKeys := yaml.sources
-	sinksConfigKeys := yaml.sinks
-	connectionsConfigKeys := yaml.connections
+	sourcesConfigKeys := yaml.Sources
+	sinksConfigKeys := yaml.Sinks
 
 	result := YamlConfigurationSet{
 		Sources:     map[string]string{},
@@ -465,13 +463,10 @@ func GetConfigurationsFor(yaml YamlConfigurationKeys) YamlConfigurationSet {
 	for key, ops := range ConfigManager.cfgOperators {
 		if strings.HasPrefix(key, ConnectionCfgOperatorKeyPrefix) {
 			plugin := strings.TrimPrefix(key, ConnectionCfgOperatorKeyPrefix)
-			keys, ok := connectionsConfigKeys[plugin]
-			if ok {
-				cfs := ops.CopyUpdatableConfContentFor(keys)
-				if len(cfs) > 0 {
-					jsonByte, _ := json.Marshal(cfs)
-					connectionResources[plugin] = string(jsonByte)
-				}
+			cfs := ops.CopyUpdatableConfContent()
+			if len(cfs) > 0 {
+				jsonByte, _ := json.Marshal(cfs)
+				connectionResources[plugin] = string(jsonByte)
 			}
 			continue
 		}
@@ -482,7 +477,7 @@ func GetConfigurationsFor(yaml YamlConfigurationKeys) YamlConfigurationSet {
 				cfs := ops.CopyUpdatableConfContentFor(keys)
 				if len(cfs) > 0 {
 					jsonByte, _ := json.Marshal(cfs)
-					connectionResources[plugin] = string(jsonByte)
+					srcResources[plugin] = string(jsonByte)
 				}
 			}
 			continue
@@ -494,7 +489,7 @@ func GetConfigurationsFor(yaml YamlConfigurationKeys) YamlConfigurationSet {
 				cfs := ops.CopyUpdatableConfContentFor(keys)
 				if len(cfs) > 0 {
 					jsonByte, _ := json.Marshal(cfs)
-					connectionResources[plugin] = string(jsonByte)
+					sinkResources[plugin] = string(jsonByte)
 				}
 			}
 			continue

+ 29 - 15
internal/plugin/native/manager.go

@@ -586,11 +586,16 @@ func (rr *Manager) Source(name string) (api.Source, error) {
 }
 
 func (rr *Manager) GetSourcePlugin(name string) (plugin2.EXTENSION_TYPE, string, string) {
-	pluginName, _ := rr.GetPluginBySymbol(plugin2.SOURCE, name)
-	var installScript = ""
-	pluginKey := plugin2.PluginTypes[plugin2.SOURCE] + "_" + pluginName
-	rr.plgInstallDb.Get(pluginKey, &installScript)
-	return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	_, ok := rr.GetPluginVersionBySymbol(plugin2.SOURCE, name)
+	if ok {
+		pluginName, _ := rr.GetPluginBySymbol(plugin2.SOURCE, name)
+		var installScript = ""
+		pluginKey := plugin2.PluginTypes[plugin2.SOURCE] + "_" + pluginName
+		rr.plgInstallDb.Get(pluginKey, &installScript)
+		return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	} else {
+		return plugin2.NONE_EXTENSION, "", ""
+	}
 }
 
 func (rr *Manager) LookupSource(name string) (api.LookupSource, error) {
@@ -632,11 +637,16 @@ func (rr *Manager) Sink(name string) (api.Sink, error) {
 }
 
 func (rr *Manager) GetSinkPlugin(name string) (plugin2.EXTENSION_TYPE, string, string) {
-	pluginName, _ := rr.GetPluginBySymbol(plugin2.SINK, name)
-	var installScript = ""
-	pluginKey := plugin2.PluginTypes[plugin2.SINK] + "_" + pluginName
-	rr.plgInstallDb.Get(pluginKey, &installScript)
-	return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	_, ok := rr.GetPluginVersionBySymbol(plugin2.SINK, name)
+	if ok {
+		pluginName, _ := rr.GetPluginBySymbol(plugin2.SINK, name)
+		var installScript = ""
+		pluginKey := plugin2.PluginTypes[plugin2.SINK] + "_" + pluginName
+		rr.plgInstallDb.Get(pluginKey, &installScript)
+		return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	} else {
+		return plugin2.NONE_EXTENSION, "", ""
+	}
 }
 
 func (rr *Manager) Function(name string) (api.Function, error) {
@@ -665,11 +675,15 @@ func (rr *Manager) HasFunctionSet(name string) bool {
 }
 
 func (rr *Manager) GetFunctionPlugin(funcName string) (plugin2.EXTENSION_TYPE, string, string) {
-	pluginName, _ := rr.GetPluginBySymbol(plugin2.FUNCTION, funcName)
-	var installScript = ""
-	pluginKey := plugin2.PluginTypes[plugin2.FUNCTION] + "_" + pluginName
-	rr.plgInstallDb.Get(pluginKey, &installScript)
-	return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	pluginName, ok := rr.GetPluginBySymbol(plugin2.FUNCTION, funcName)
+	if ok {
+		var installScript = ""
+		pluginKey := plugin2.PluginTypes[plugin2.FUNCTION] + "_" + pluginName
+		rr.plgInstallDb.Get(pluginKey, &installScript)
+		return plugin2.NATIVE_EXTENSION, pluginKey, installScript
+	} else {
+		return plugin2.NONE_EXTENSION, "", ""
+	}
 }
 
 func (rr *Manager) ConvName(name string) (string, bool) {

+ 2 - 1
internal/plugin/plugin.go

@@ -102,7 +102,8 @@ func (fp *FuncPlugin) GetSymbols() []string {
 type EXTENSION_TYPE int
 
 const (
-	INTERNAL EXTENSION_TYPE = iota
+	NONE_EXTENSION EXTENSION_TYPE = iota
+	INTERNAL
 	NATIVE_EXTENSION
 	PORTABLE_EXTENSION
 	SERVICE_EXTENSION

+ 24 - 12
internal/plugin/portable/factory.go

@@ -31,10 +31,14 @@ func (m *Manager) Source(name string) (api.Source, error) {
 }
 
 func (m *Manager) GetSourcePlugin(name string) (plugin.EXTENSION_TYPE, string, string) {
-	pluginName, _ := m.reg.GetSymbol(plugin.SOURCE, name)
-	var installScript = ""
-	m.plgInstallDb.Get(pluginName, &installScript)
-	return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	pluginName, ok := m.reg.GetSymbol(plugin.SOURCE, name)
+	if ok {
+		var installScript = ""
+		m.plgInstallDb.Get(pluginName, &installScript)
+		return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	} else {
+		return plugin.NONE_EXTENSION, "", ""
+	}
 }
 
 func (m *Manager) LookupSource(_ string) (api.LookupSource, error) {
@@ -51,10 +55,14 @@ func (m *Manager) Sink(name string) (api.Sink, error) {
 }
 
 func (m *Manager) GetSinkPlugin(name string) (plugin.EXTENSION_TYPE, string, string) {
-	pluginName, _ := m.reg.GetSymbol(plugin.SINK, name)
-	var installScript = ""
-	m.plgInstallDb.Get(pluginName, &installScript)
-	return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	pluginName, ok := m.reg.GetSymbol(plugin.SINK, name)
+	if ok {
+		var installScript = ""
+		m.plgInstallDb.Get(pluginName, &installScript)
+		return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	} else {
+		return plugin.NONE_EXTENSION, "", ""
+	}
 }
 
 // The function instance are kept forever even after deletion
@@ -87,10 +95,14 @@ func (m *Manager) HasFunctionSet(funcName string) bool {
 }
 
 func (m *Manager) GetFunctionPlugin(funcName string) (plugin.EXTENSION_TYPE, string, string) {
-	pluginName, _ := m.reg.GetSymbol(plugin.FUNCTION, funcName)
-	var installScript = ""
-	m.plgInstallDb.Get(pluginName, &installScript)
-	return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	pluginName, ok := m.reg.GetSymbol(plugin.FUNCTION, funcName)
+	if ok {
+		var installScript = ""
+		m.plgInstallDb.Get(pluginName, &installScript)
+		return plugin.PORTABLE_EXTENSION, pluginName, installScript
+	} else {
+		return plugin.NONE_EXTENSION, "", ""
+	}
 }
 
 func (m *Manager) ConvName(funcName string) (string, bool) {

+ 1 - 1
internal/plugin/wasm/factory.go

@@ -40,7 +40,7 @@ func (m *Manager) HasFunctionSet(funcName string) bool {
 }
 
 func (m *Manager) GetFunctionPlugin(funcName string) (plugin.EXTENSION_TYPE, string, string) {
-	return plugin.WASM_EXTENSION, "", ""
+	return plugin.NONE_EXTENSION, "", ""
 }
 
 func (m *Manager) ConvName(funcName string) (string, bool) {

+ 273 - 0
internal/processor/rule_migration.go

@@ -0,0 +1,273 @@
+// Copyright 2023 EMQ Technologies Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package processor
+
+import (
+	"encoding/json"
+	"github.com/lf-edge/ekuiper/internal/binder/function"
+	"github.com/lf-edge/ekuiper/internal/binder/io"
+	"github.com/lf-edge/ekuiper/internal/meta"
+	store2 "github.com/lf-edge/ekuiper/internal/pkg/store"
+	"github.com/lf-edge/ekuiper/internal/plugin"
+	"github.com/lf-edge/ekuiper/internal/schema"
+	"github.com/lf-edge/ekuiper/internal/topo/node/conf"
+	"github.com/lf-edge/ekuiper/internal/xsql"
+	"github.com/lf-edge/ekuiper/pkg/api"
+	"github.com/lf-edge/ekuiper/pkg/ast"
+	"strings"
+)
+
+type RuleMigrationProcessor struct {
+	r *RuleProcessor
+	s *StreamProcessor
+}
+
+func NewRuleMigrationProcessor(r *RuleProcessor, s *StreamProcessor) *RuleMigrationProcessor {
+	return &RuleMigrationProcessor{
+		r: r,
+		s: s,
+	}
+}
+
+func NewDependencies() *Dependencies {
+	return &Dependencies{
+		SourceConfigKeys: map[string][]string{},
+		SinkConfigKeys:   map[string][]string{},
+	}
+}
+
+// Dependencies copy all connections related configs by hardcode
+type Dependencies struct {
+	Rules            []string
+	Streams          []string
+	Tables           []string
+	Sources          []string
+	Sinks            []string
+	SourceConfigKeys map[string][]string
+	SinkConfigKeys   map[string][]string
+	Functions        []string
+	Schemas          []string
+}
+
+func ruleTraverse(rule *api.Rule, de *Dependencies) {
+	sql := rule.Sql
+	if sql != "" {
+		stmt, err := xsql.GetStatementFromSql(sql)
+		if err != nil {
+			return
+		}
+		err, store := store2.GetKV("stream")
+		if err != nil {
+			return
+		}
+		//streams
+		streamsFromStmt := xsql.GetStreams(stmt)
+		for _, s := range streamsFromStmt {
+			streamStmt, err := xsql.GetDataSource(store, s)
+			if err != nil {
+				continue
+			}
+			if streamStmt.StreamType == ast.TypeStream {
+				//get streams
+				de.Streams = append(de.Streams, string(streamStmt.Name))
+			} else if streamStmt.StreamType == ast.TypeTable {
+				//get tables
+				de.Tables = append(de.Tables, string(streamStmt.Name))
+			}
+
+			//get source type
+			de.Sources = append(de.Sources, streamStmt.Options.TYPE)
+			//get config key
+			_, ok := de.SourceConfigKeys[streamStmt.Options.TYPE]
+			if ok {
+				de.SourceConfigKeys[streamStmt.Options.TYPE] = append(de.SourceConfigKeys[streamStmt.Options.TYPE], streamStmt.Options.CONF_KEY)
+			} else {
+				var confKeys []string
+				confKeys = append(confKeys, streamStmt.Options.CONF_KEY)
+				de.SourceConfigKeys[streamStmt.Options.TYPE] = confKeys
+			}
+
+			//get schema id
+			if streamStmt.Options.SCHEMAID != "" {
+				r := strings.Split(streamStmt.Options.SCHEMAID, ".")
+				de.Schemas = append(de.Schemas, streamStmt.Options.FORMAT+"_"+r[0])
+			}
+		}
+		//actions
+		for _, m := range rule.Actions {
+			for name, action := range m {
+				props, _ := action.(map[string]interface{})
+				de.Sinks = append(de.Sinks, name)
+				resourceId, ok := props[conf.ResourceID].(string)
+				if ok {
+					_, ok := de.SinkConfigKeys[name]
+					if ok {
+						de.SinkConfigKeys[name] = append(de.SinkConfigKeys[name], resourceId)
+					} else {
+						var confKeys []string
+						confKeys = append(confKeys, resourceId)
+						de.SinkConfigKeys[name] = confKeys
+					}
+				}
+
+				format, ok := props["format"].(string)
+				if ok && format != "json" {
+					schemaId, ok := props["schemaId"].(string)
+					if ok {
+						r := strings.Split(schemaId, ".")
+						de.Schemas = append(de.Schemas, format+"_"+r[0])
+					}
+				}
+			}
+		}
+		// function
+		ast.WalkFunc(stmt, func(n ast.Node) bool {
+			switch f := n.(type) {
+			case *ast.Call:
+				de.Functions = append(de.Functions, f.Name)
+			}
+			return true
+		})
+
+		//Rules
+		de.Rules = append(de.Rules, rule.Id)
+	}
+}
+
+type Configuration struct {
+	Streams          map[string]string `json:"streams"`
+	Tables           map[string]string `json:"tables"`
+	Rules            map[string]string `json:"rules"`
+	NativePlugins    map[string]string `json:"nativePlugins"`
+	PortablePlugins  map[string]string `json:"portablePlugins"`
+	SourceConfig     map[string]string `json:"sourceConfig"`
+	SinkConfig       map[string]string `json:"sinkConfig"`
+	ConnectionConfig map[string]string `json:"connectionConfig"`
+	Service          map[string]string `json:"Service"`
+	Schema           map[string]string `json:"Schema"`
+}
+
+func (p *RuleMigrationProcessor) ConfigurationPartialExport(rules []string) ([]byte, error) {
+	conf := &Configuration{
+		Streams:          make(map[string]string),
+		Tables:           make(map[string]string),
+		Rules:            make(map[string]string),
+		NativePlugins:    make(map[string]string),
+		PortablePlugins:  make(map[string]string),
+		SourceConfig:     make(map[string]string),
+		SinkConfig:       make(map[string]string),
+		ConnectionConfig: make(map[string]string),
+		Service:          make(map[string]string),
+		Schema:           make(map[string]string),
+	}
+	conf.Rules = p.exportRules(rules)
+
+	de := NewDependencies()
+	for _, v := range rules {
+		rule, _ := p.r.GetRuleById(v)
+		if rule != nil {
+			ruleTraverse(rule, de)
+		}
+	}
+
+	p.exportSelected(de, conf)
+
+	return json.Marshal(conf)
+}
+
+func (p *RuleMigrationProcessor) exportRules(rules []string) map[string]string {
+	ruleSet := make(map[string]string)
+
+	for _, v := range rules {
+		ruleJson, _ := p.r.GetRuleJson(v)
+		ruleSet[v] = ruleJson
+	}
+	return ruleSet
+}
+
+func (p *RuleMigrationProcessor) exportStreams(streams []string) map[string]string {
+	streamSet := make(map[string]string)
+
+	for _, v := range streams {
+		streamJson, _ := p.s.GetStream(v, ast.TypeStream)
+		streamSet[v] = streamJson
+	}
+	return streamSet
+}
+
+func (p *RuleMigrationProcessor) exportTables(tables []string) map[string]string {
+	tableSet := make(map[string]string)
+
+	for _, v := range tables {
+		tableJson, _ := p.s.GetStream(v, ast.TypeTable)
+		tableSet[v] = tableJson
+	}
+	return tableSet
+}
+
+func (p *RuleMigrationProcessor) exportSelected(de *Dependencies, config *Configuration) {
+	//get the stream and table
+	config.Streams = p.exportStreams(de.Streams)
+	config.Tables = p.exportTables(de.Tables)
+	//get the sources
+	for _, v := range de.Sources {
+		t, srcName, srcInfo := io.GetSourcePlugin(v)
+		if t == plugin.NATIVE_EXTENSION {
+			config.NativePlugins[srcName] = srcInfo
+		}
+		if t == plugin.PORTABLE_EXTENSION {
+			config.PortablePlugins[srcName] = srcInfo
+		}
+	}
+	// get sinks
+	for _, v := range de.Sinks {
+		t, sinkName, sinkInfo := io.GetSinkPlugin(v)
+		if t == plugin.NATIVE_EXTENSION {
+			config.NativePlugins[sinkName] = sinkInfo
+		}
+		if t == plugin.PORTABLE_EXTENSION {
+			config.PortablePlugins[sinkName] = sinkInfo
+		}
+	}
+
+	// get functions
+	for _, v := range de.Functions {
+		t, svcName, svcInfo := function.GetFunctionPlugin(v)
+		if t == plugin.NATIVE_EXTENSION {
+			config.NativePlugins[svcName] = svcInfo
+		}
+		if t == plugin.PORTABLE_EXTENSION {
+			config.PortablePlugins[svcName] = svcInfo
+		}
+		if t == plugin.SERVICE_EXTENSION {
+			config.Service[svcName] = svcInfo
+		}
+	}
+
+	// get sourceCfg/sinkCfg
+	configKeys := meta.YamlConfigurationKeys{}
+	configKeys.Sources = de.SourceConfigKeys
+	configKeys.Sinks = de.SinkConfigKeys
+	configSet := meta.GetConfigurationsFor(configKeys)
+	config.SourceConfig = configSet.Sources
+	config.SinkConfig = configSet.Sinks
+	config.ConnectionConfig = configSet.Connections
+
+	//get schema
+	for _, v := range de.Schemas {
+		schName, schInfo := schema.GetSchemaInstallScript(v)
+		config.Schema[schName] = schInfo
+	}
+}

+ 5 - 5
internal/processor/ruleset.go

@@ -30,7 +30,7 @@ type RulesetProcessor struct {
 type Ruleset struct {
 	Streams map[string]string `json:"streams"`
 	Tables  map[string]string `json:"tables"`
-	Rules   map[string]string `json:"rules"`
+	Rules   map[string]string `json:"Rules"`
 }
 
 func NewRulesetProcessor(r *RuleProcessor, s *StreamProcessor) *RulesetProcessor {
@@ -50,7 +50,7 @@ func (rs *RulesetProcessor) Export() (io.ReadSeeker, []int, error) {
 	all.Tables = allStreams["tables"]
 	rules, err := rs.r.GetAllRulesJson()
 	if err != nil {
-		return nil, nil, fmt.Errorf("fail to get all rules: %v", err)
+		return nil, nil, fmt.Errorf("fail to get all Rules: %v", err)
 	}
 	all.Rules = rules
 	jsonBytes, err := json.Marshal(all)
@@ -72,7 +72,7 @@ func (rs *RulesetProcessor) ExportRuleSet() *Ruleset {
 	all.Tables = allStreams["tables"]
 	rules, err := rs.r.GetAllRulesJson()
 	if err != nil {
-		conf.Log.Errorf("fail to get all rules: %v", err)
+		conf.Log.Errorf("fail to get all Rules: %v", err)
 		return nil
 	}
 	all.Rules = rules
@@ -128,7 +128,7 @@ func (rs *RulesetProcessor) Import(content []byte) ([]string, []int, error) {
 		}
 	}
 	var rules []string
-	// restore rules
+	// restore Rules
 	for k, v := range all.Rules {
 		_, e := rs.r.ExecCreateWithValidation(k, v)
 		if e != nil {
@@ -170,7 +170,7 @@ func (rs *RulesetProcessor) ImportRuleSet(all Ruleset) {
 		}
 	}
 	var rules []string
-	// restore rules
+	// restore Rules
 	for k, v := range all.Rules {
 		_, e := rs.r.ExecCreateWithValidation(k, v)
 		if e != nil {

+ 5 - 5
internal/processor/ruleset_test.go

@@ -25,7 +25,7 @@ import (
 )
 
 func TestIO(t *testing.T) {
-	expected := `{"streams":{"demo":"CREATE STREAM demo () WITH (DATASOURCE=\"users\", FORMAT=\"JSON\")"},"tables":{},"rules":{"rule1":"{\"id\":\"rule1\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{\"log\": {}}]}","rule2":"{\"id\": \"rule2\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{  \"log\": {}}]}"}}`
+	expected := `{"streams":{"demo":"CREATE STREAM demo () WITH (DATASOURCE=\"users\", FORMAT=\"JSON\")"},"tables":{},"Rules":{"rule1":"{\"id\":\"rule1\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{\"log\": {}}]}","rule2":"{\"id\": \"rule2\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{  \"log\": {}}]}"}}`
 	expectedCounts := []int{1, 0, 2}
 	expectedStreams := []string{"demo"}
 	expectedRules := []string{"rule1", "rule2"}
@@ -42,7 +42,7 @@ func TestIO(t *testing.T) {
 	}
 	sort.Strings(names)
 	if !reflect.DeepEqual(names, expectedRules) {
-		t.Errorf("fail to return the imported rules, expect %v but got %v", expectedRules, names)
+		t.Errorf("fail to return the imported Rules, expect %v but got %v", expectedRules, names)
 	}
 	if !reflect.DeepEqual(counts, expectedCounts) {
 		t.Errorf("fail to return the correct counts, expect %v, but got %v", expectedCounts, counts)
@@ -60,12 +60,12 @@ func TestIO(t *testing.T) {
 
 	rules, err := rp.GetAllRules()
 	if err != nil {
-		t.Errorf("fail to get all rules: %v", err)
+		t.Errorf("fail to get all Rules: %v", err)
 		return
 	}
 	sort.Strings(rules)
 	if !reflect.DeepEqual(rules, expectedRules) {
-		t.Errorf("After import, expect rules %v, but got %v", expectedRules, rules)
+		t.Errorf("After import, expect Rules %v, but got %v", expectedRules, rules)
 		return
 	}
 
@@ -92,7 +92,7 @@ func TestIO(t *testing.T) {
 func TestImportError(t *testing.T) {
 	contents := []string{
 		"notjson",
-		`{INvalid"streams":{"demo":"CREATE STREAM demo () WITH (DATASOURCE=\"users\", FORMAT=\"JSON\")"},"tables":{},"rules":{"rule1":"{\"id\":\"rule1\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{\"log\": {}}]}","rule2":"{\"id\": \"rule2\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{  \"log\": {}}]}"}}`,
+		`{INvalid"streams":{"demo":"CREATE STREAM demo () WITH (DATASOURCE=\"users\", FORMAT=\"JSON\")"},"tables":{},"Rules":{"rule1":"{\"id\":\"rule1\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{\"log\": {}}]}","rule2":"{\"id\": \"rule2\",\"sql\": \"SELECT * FROM demo\",\"actions\": [{  \"log\": {}}]}"}}`,
 	}
 	sp := NewStreamProcessor()
 	defer sp.db.Clean()

+ 6 - 6
internal/processor/stream.go

@@ -265,7 +265,7 @@ func (p *StreamProcessor) ShowTable(kind string) ([]string, error) {
 	return result, nil
 }
 
-func (p *StreamProcessor) getStream(name string, st ast.StreamType) (string, error) {
+func (p *StreamProcessor) GetStream(name string, st ast.StreamType) (string, error) {
 	vs, err := xsql.GetDataSourceStatement(p.db, name)
 	if vs != nil && vs.StreamType == st {
 		return vs.Statement, nil
@@ -336,7 +336,7 @@ func printOptions(opts *ast.Options, buff *bytes.Buffer) {
 }
 
 func (p *StreamProcessor) DescStream(name string, st ast.StreamType) (ast.Statement, error) {
-	statement, err := p.getStream(name, st)
+	statement, err := p.GetStream(name, st)
 	if err != nil {
 		return nil, fmt.Errorf("Describe %s fails, %s.", ast.StreamTypeMap[st], err)
 	}
@@ -349,7 +349,7 @@ func (p *StreamProcessor) DescStream(name string, st ast.StreamType) (ast.Statem
 }
 
 func (p *StreamProcessor) GetInferredSchema(name string, st ast.StreamType) (ast.StreamFields, error) {
-	statement, err := p.getStream(name, st)
+	statement, err := p.GetStream(name, st)
 	if err != nil {
 		return nil, fmt.Errorf("Describe %s fails, %s.", ast.StreamTypeMap[st], err)
 	}
@@ -370,7 +370,7 @@ func (p *StreamProcessor) GetInferredSchema(name string, st ast.StreamType) (ast
 
 // GetInferredJsonSchema return schema in json schema type
 func (p *StreamProcessor) GetInferredJsonSchema(name string, st ast.StreamType) (map[string]*ast.JsonStreamField, error) {
-	statement, err := p.getStream(name, st)
+	statement, err := p.GetStream(name, st)
 	if err != nil {
 		return nil, fmt.Errorf("Describe %s fails, %s.", ast.StreamTypeMap[st], err)
 	}
@@ -394,7 +394,7 @@ func (p *StreamProcessor) GetInferredJsonSchema(name string, st ast.StreamType)
 }
 
 func (p *StreamProcessor) execExplain(stmt ast.NameNode, st ast.StreamType) (string, error) {
-	_, err := p.getStream(stmt.GetName(), st)
+	_, err := p.GetStream(stmt.GetName(), st)
 	if err != nil {
 		return "", fmt.Errorf("Explain %s fails, %s.", ast.StreamTypeMap[st], err)
 	}
@@ -416,7 +416,7 @@ func (p *StreamProcessor) DropStream(name string, st ast.StreamType) (string, er
 			return "", err
 		}
 	}
-	_, err := p.getStream(name, st)
+	_, err := p.GetStream(name, st)
 	if err != nil {
 		return "", err
 	}

+ 1 - 2
internal/schema/registry.go

@@ -334,8 +334,7 @@ func removeSchemaInstallScript(schemaType def.SchemaType, name string) {
 	_ = schemaDb.Delete(key)
 }
 
-func GetSchemaInstallScript(schemaId string) (string, string) {
-	key := strings.ReplaceAll(schemaId, ".", "_")
+func GetSchemaInstallScript(key string) (string, string) {
 	var script string
 	schemaDb.Get(key, &script)
 	return key, script

+ 13 - 0
internal/server/rest.go

@@ -141,6 +141,7 @@ func createRestServer(ip string, port int, needToken bool) *http.Server {
 	r.HandleFunc("/config/uploads", fileUploadHandler).Methods(http.MethodPost, http.MethodGet)
 	r.HandleFunc("/config/uploads/{name}", fileDeleteHandler).Methods(http.MethodDelete)
 	r.HandleFunc("/data/export", configurationExportHandler).Methods(http.MethodGet)
+	r.HandleFunc("/data/partial/export", configurationPartialExportHandler).Methods(http.MethodPost)
 	r.HandleFunc("/data/import", configurationImportHandler).Methods(http.MethodPost)
 	r.HandleFunc("/data/import/status", configurationStatusHandler).Methods(http.MethodGet)
 	// Register extended routes
@@ -690,6 +691,18 @@ func configurationExportHandler(w http.ResponseWriter, r *http.Request) {
 	http.ServeContent(w, r, name, time.Now(), bytes.NewReader(jsonBytes))
 }
 
+func configurationPartialExportHandler(w http.ResponseWriter, r *http.Request) {
+	const name = "ekuiper_export.json"
+
+	var rules []string
+	_ = json.NewDecoder(r.Body).Decode(&rules)
+
+	jsonBytes, _ := ruleMigrationProcessor.ConfigurationPartialExport(rules)
+	w.Header().Set("Content-Type", "application/octet-stream")
+	w.Header().Add("Content-Disposition", "Attachment")
+	http.ServeContent(w, r, name, time.Now(), bytes.NewReader(jsonBytes))
+}
+
 func configurationReset() {
 	_ = resetAllRules()
 	_ = resetAllStreams()

+ 8 - 6
internal/server/server.go

@@ -38,12 +38,13 @@ import (
 )
 
 var (
-	logger           = conf.Log
-	startTimeStamp   int64
-	version          = ""
-	ruleProcessor    *processor.RuleProcessor
-	streamProcessor  *processor.StreamProcessor
-	rulesetProcessor *processor.RulesetProcessor
+	logger                 = conf.Log
+	startTimeStamp         int64
+	version                = ""
+	ruleProcessor          *processor.RuleProcessor
+	streamProcessor        *processor.StreamProcessor
+	rulesetProcessor       *processor.RulesetProcessor
+	ruleMigrationProcessor *processor.RuleMigrationProcessor
 )
 
 // Create path if mount an empty dir. For edgeX, all the folders must be created priorly
@@ -94,6 +95,7 @@ func StartUp(Version, LoadFileType string) {
 	ruleProcessor = processor.NewRuleProcessor()
 	streamProcessor = processor.NewStreamProcessor()
 	rulesetProcessor = processor.NewRulesetProcessor(ruleProcessor, streamProcessor)
+	ruleMigrationProcessor = processor.NewRuleMigrationProcessor(ruleProcessor, streamProcessor)
 
 	// register all extensions
 	for k, v := range components {

+ 8 - 4
internal/service/manager.go

@@ -202,10 +202,14 @@ func (m *Manager) HasFunctionSet(_ string) bool {
 }
 
 func (m *Manager) GetFunctionPlugin(funcName string) (plugin.EXTENSION_TYPE, string, string) {
-	funcContainer, _ := m.getFunction(funcName)
-	var installScript = ""
-	m.serviceInstallKV.Get(funcContainer.ServiceName, &installScript)
-	return plugin.SERVICE_EXTENSION, funcContainer.ServiceName, installScript
+	funcContainer, ok := m.getFunction(funcName)
+	if ok {
+		var installScript = ""
+		m.serviceInstallKV.Get(funcContainer.ServiceName, &installScript)
+		return plugin.SERVICE_EXTENSION, funcContainer.ServiceName, installScript
+	} else {
+		return plugin.NONE_EXTENSION, "", ""
+	}
 }
 
 func (m *Manager) Function(name string) (api.Function, error) {