Skip to content

Commit

Permalink
Automatically resolve dependencies for language expressions
Browse files Browse the repository at this point in the history
fixes #914
fixes #929
  • Loading branch information
jamesnetherton authored and lburgazzoli committed Sep 26, 2019
1 parent 19b1f36 commit 5ceee9f
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 10 deletions.
148 changes: 145 additions & 3 deletions pkg/metadata/metadata_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,55 @@ func TestJacksonDependency(t *testing.T) {
assert.ElementsMatch(t, []string{"camel:http4", "camel:jackson", "camel:log"}, meta.Dependencies.List())
}

func TestLanguageDependencies(t *testing.T) {
code := v1alpha1.SourceSpec{
DataSpec: v1alpha1.DataSpec{
Name: "Languages.java",
Content: `
from("direct:start")
.transform().ognl("request.body.name == 'Camel K'")
.transform().simple("${body.toUpperCase()}")
.transform().mvel("resource:classpath:script.mvel")
.transform().xquery("/ns:foo/bar", String.class, new Namespaces("ns", "http://foo/bar"))
.transform().xpath("//foo/bar")
.transform().jsonpath("$.foo")
.transform().groovy("request.body += 'modified'")
.split().xtokenize("/ns:foo/bar", new Namespaces("ns", "http://foo/bar"));
`,
},
Language: v1alpha1.LanguageJavaSource,
}

catalog, err := test.DefaultCatalog()
assert.Nil(t, err)

meta := Extract(catalog, code)
assert.ElementsMatch(t, []string{"camel:direct", "camel:bean", "camel:ognl", "camel:saxon", "camel:xpath",
"camel:jsonpath", "camel:groovy", "camel:jaxp", "camel:mvel"}, meta.Dependencies.List())
}

func TestLanguageDependenciesTransformExpression(t *testing.T) {
code := v1alpha1.SourceSpec{
DataSpec: v1alpha1.DataSpec{
Name: "Languages.java",
Content: `
from("direct:start")
.transform(language("ognl", "request.body.name == 'Camel K'"))
.transform(simple("${body.toUpperCase()}"))
.transform(xpath("//foo/bar"))
.transform(jsonpath("$.foo"))
`,
},
Language: v1alpha1.LanguageJavaSource,
}

catalog, err := test.DefaultCatalog()
assert.Nil(t, err)

meta := Extract(catalog, code)
assert.ElementsMatch(t, []string{"camel:direct", "camel:bean", "camel:ognl", "camel:xpath", "camel:jsonpath"}, meta.Dependencies.List())
}

func TestHystrixDependency(t *testing.T) {
code := v1alpha1.SourceSpec{
DataSpec: v1alpha1.DataSpec{
Expand Down Expand Up @@ -232,7 +281,7 @@ func TestRestClosureDependency(t *testing.T) {
Name: "Request.groovy",
Content: `
rest {
}
}
from("http4:test")
.to("log:info")
`,
Expand Down Expand Up @@ -304,19 +353,94 @@ func TestXMLRestDependency(t *testing.T) {
assert.ElementsMatch(t, []string{"camel:direct", "camel:rest", "camel:mock"}, meta.Dependencies.List())
}

func TestXMLLanguageDependencies(t *testing.T) {
code := v1alpha1.SourceSpec{
DataSpec: v1alpha1.DataSpec{
Name: "routes.xml",
Content: `
<from uri="direct:start" />
<transform>
<language language="ognl">request.body.name == 'Camel K'</language>
</transform>
<transform>
<simple>${body.toUpperCase()}</simple>
</transform>
<transform>
<mvel>resource:classpath:script.mvel</mvel>
</transform>
<transform>
<jsonpath>$.foo</jsonpath>
</transform>
<transform>
<groovy>request.body += 'modified'</groovy>
</transform>
<transform>
<tokenize>request.body += 'modified'</tokenize>
</transform>
<transform>
<xtokenize>/ns:foo/bar</xtokenize>
</transform>
<transform>
<xpath>//foo/bar</xpath>
</transform>
<transform>
<xquery>//ns:foo/bar</xquery>
</transform>
<split>
<tokenize token=","/>
</split>
`,
},
Language: v1alpha1.LanguageXML,
}

catalog, err := test.DefaultCatalog()
assert.Nil(t, err)

meta := Extract(catalog, code)
assert.ElementsMatch(t, []string{"camel:direct", "camel:bean", "camel:ognl", "camel:saxon", "camel:xpath",
"camel:jsonpath", "camel:groovy", "camel:jaxp", "camel:mvel"}, meta.Dependencies.List())
}

const yamlWithRest = `
- rest:
path: "/"
steps:
- to: "log:info"
- to: "direct:hello"
- to: "direct:hello"
`
const yamlWithHystrix = `
- from:
uri: "direct:start"
steps:
- hystrix:
todo: "not implemented"
todo: "not implemented"
`

const yamlWithLanguages = `
- from:
uri: "direct:start"
steps:
- set-body:
constant: "Hello Camel K"
- transform:
language:
language: "ognl"
expression: "request.body.name == 'Camel K'"
- transform:
simple: "${body.toUpperCase()}"
- transform:
mvel: "resource:classpath:script.mvel"
- transform:
xquery: "/ns:foo/bar"
- transform:
xpath: "//foo/bar"
- transform:
jsonpath: "$.foo"
- transform:
groovy: "request.body += 'modified'"
- split:
xtokenize: "/ns:foo/bar"
`

func TestYAMLRestDependency(t *testing.T) {
Expand Down Expand Up @@ -354,3 +478,21 @@ func TestYAMLHystrixDependency(t *testing.T) {

assert.ElementsMatch(t, []string{"camel:direct", "camel:hystrix"}, meta.Dependencies.List())
}

func TestYAMLLanguageDependencies(t *testing.T) {
code := v1alpha1.SourceSpec{
DataSpec: v1alpha1.DataSpec{
Name: "routes.yaml",
Content: yamlWithLanguages,
},
Language: v1alpha1.LanguageYaml,
}

catalog, err := test.DefaultCatalog()
assert.Nil(t, err)

meta := Extract(catalog, code)

assert.ElementsMatch(t, []string{"camel:direct", "camel:bean", "camel:ognl", "camel:saxon", "camel:xpath",
"camel:jsonpath", "camel:groovy", "camel:jaxp", "camel:mvel"}, meta.Dependencies.List())
}
18 changes: 16 additions & 2 deletions pkg/util/camel/camel_runtime_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ func NewRuntimeCatalog(spec v1alpha1.CamelCatalogSpec) *RuntimeCatalog {
catalog.CamelCatalogSpec = spec
catalog.artifactByScheme = make(map[string]string)
catalog.schemesByID = make(map[string]v1alpha1.CamelScheme)
catalog.languageDependencies = make(map[string]string)

for id, artifact := range catalog.Artifacts {
for _, scheme := range artifact.Schemes {
scheme := scheme
catalog.artifactByScheme[scheme.ID] = id
catalog.schemesByID[scheme.ID] = scheme
}
for _, language := range artifact.Languages {
// Skip languages in common dependencies since they are always available to integrations
if artifact.ArtifactID != "camel-base" {
catalog.languageDependencies[language] = strings.Replace(artifact.ArtifactID, "camel-", "camel:", 1)
}
}
}

return &catalog
Expand All @@ -45,8 +52,9 @@ func NewRuntimeCatalog(spec v1alpha1.CamelCatalogSpec) *RuntimeCatalog {
type RuntimeCatalog struct {
v1alpha1.CamelCatalogSpec

artifactByScheme map[string]string
schemesByID map[string]v1alpha1.CamelScheme
artifactByScheme map[string]string
schemesByID map[string]v1alpha1.CamelScheme
languageDependencies map[string]string
}

// HasArtifact --
Expand Down Expand Up @@ -76,6 +84,12 @@ func (c *RuntimeCatalog) GetScheme(id string) (v1alpha1.CamelScheme, bool) {
return scheme, ok
}

// GetLanguageDependency returns the maven dependency for the given language name
func (c *RuntimeCatalog) GetLanguageDependency(language string) (string, bool) {
language, ok := c.languageDependencies[language]
return language, ok
}

// VisitArtifacts --
func (c *RuntimeCatalog) VisitArtifacts(visitor func(string, v1alpha1.CamelArtifact) bool) {
for id, artifact := range c.Artifacts {
Expand Down
27 changes: 22 additions & 5 deletions pkg/util/source/inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,22 @@ var (
doubleQuotedTo = regexp.MustCompile(`\.to\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`)
doubleQuotedToD = regexp.MustCompile(`\.toD\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`)
doubleQuotedToF = regexp.MustCompile(`\.toF\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`)
languageRegexp = regexp.MustCompile(`language\s*\(\s*["|']([a-zA-Z0-9-]+[^"|']+)["|']\s*,.*\)`)

additionalDependencies = map[string]string{
`.*JsonLibrary\.Jackson.*`: "camel:jackson",
`.*\.hystrix().*`: "camel:hystrix",
`.*restConfiguration().*`: "camel:rest",
`.*rest(("[a-zA-Z0-9-/]+")*).*`: "camel:rest",
`^\s*rest\s*{.*`: "camel:rest",
`.*JsonLibrary\.Jackson.*`: "camel:jackson",
`.*\.hystrix().*`: "camel:hystrix",
`.*restConfiguration().*`: "camel:rest",
`.*rest(("[a-zA-Z0-9-/]+")*).*`: "camel:rest",
`^\s*rest\s*{.*`: "camel:rest",
`.*\.groovy\s*\(.*\).*`: "camel:groovy",
`.*\.?(jsonpath|jsonpathWriteAsString)\s*\(.*\).*`: "camel:jsonpath",
`.*\.ognl\s*\(.*\).*`: "camel:ognl",
`.*\.mvel\s*\(.*\).*`: "camel:mvel",
`.*\.?simple\s*\(.*\).*`: "camel:bean",
`.*\.xquery\s*\(.*\).*`: "camel:saxon",
`.*\.?xpath\s*\(.*\).*`: "camel:xpath",
`.*\.xtokenize\s*\(.*\).*`: "camel:jaxp",
}
)

Expand Down Expand Up @@ -118,6 +127,14 @@ func (i *baseInspector) discoverDependencies(source v1alpha1.SourceSpec, meta *M
meta.Dependencies.Add(dep)
}
}

for _, match := range languageRegexp.FindAllStringSubmatch(source.Content, -1) {
if len(match) > 1 {
if dependency, ok := i.catalog.GetLanguageDependency(match[1]); ok {
meta.Dependencies.Add(dependency)
}
}
}
}

func (i *baseInspector) decodeComponent(uri string) string {
Expand Down
14 changes: 14 additions & 0 deletions pkg/util/source/inspector_xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ func (i XMLInspector) Extract(source v1alpha1.SourceSpec, meta *Metadata) error
meta.Dependencies.Add("camel:rest")
case "hystrix":
meta.Dependencies.Add("camel:hystrix")
case "simple":
meta.Dependencies.Add("camel:bean")
case "language":
for _, a := range se.Attr {
if a.Name.Local == "language" {
if dependency, ok := i.catalog.GetLanguageDependency(a.Value); ok {
meta.Dependencies.Add(dependency)
}
}
}
case "from", "fromF":
for _, a := range se.Attr {
if a.Name.Local == "uri" {
Expand All @@ -60,6 +70,10 @@ func (i XMLInspector) Extract(source v1alpha1.SourceSpec, meta *Metadata) error
}
}
}

if dependency, ok := i.catalog.GetLanguageDependency(se.Name.Local); ok {
meta.Dependencies.Add(dependency)
}
}
}

Expand Down
25 changes: 25 additions & 0 deletions pkg/util/source/inspector_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ func (inspector YAMLInspector) parseStep(key string, content interface{}, meta *
if u, ok := t["uri"]; ok {
maybeURI = u.(string)
}

if _, ok := t["simple"]; ok {
meta.Dependencies.Add("camel:bean")
}

if _, ok := t["language"]; ok {
if s, ok := t["language"].(string); ok {
if dependency, ok := inspector.catalog.GetLanguageDependency(s); ok {
meta.Dependencies.Add(dependency)
}
} else if m, ok := t["language"].(map[interface{}]interface{}); ok {
if err := inspector.parseStep("language", m, meta); err != nil {
return err
}
}
}

for k := range t {
if s, ok := k.(string); ok {
if dependency, ok := inspector.catalog.GetLanguageDependency(s); ok {
meta.Dependencies.Add(dependency)
}
}
}

if u, ok := t["steps"]; ok {
steps := u.([]interface{})

Expand Down

0 comments on commit 5ceee9f

Please sign in to comment.