From 8776dc90d0a3cbf2d6936b2ece6908bc27aacca6 Mon Sep 17 00:00:00 2001 From: Maria Arias de Reyna Date: Mon, 19 Dec 2022 11:19:46 +0100 Subject: [PATCH] feature(eip): Add Try-Catch EIP --- .../backend/api/resource/eip.kamelet.yaml | 21 +++ .../java/io/kaoto/backend/KamelPopulator.java | 4 + .../generator/kamelet/KameletRepresenter.java | 2 + .../kamelet/step/CircuitBreaker.java | 10 +- .../deployment/kamelet/step/DoCatch.java | 72 ++++++++ .../kamelet/step/FlowStepDeserializer.java | 2 + ...allback.java => GenericFlowWithSteps.java} | 8 +- .../deployment/kamelet/step/TryCatch.java | 169 ++++++++++++++++++ .../kamelet/step/TryCatchFlowStep.java | 55 ++++++ .../step/parser/kamelet/eip.kamelet.yaml | 31 ++-- 10 files changed, 353 insertions(+), 21 deletions(-) create mode 100644 kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/DoCatch.java rename kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/{CircuitBreakerOnFallback.java => GenericFlowWithSteps.java} (71%) create mode 100644 kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatch.java create mode 100644 kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatchFlowStep.java diff --git a/api/src/test/resources/io/kaoto/backend/api/resource/eip.kamelet.yaml b/api/src/test/resources/io/kaoto/backend/api/resource/eip.kamelet.yaml index 4ac919fe1..f072fd435 100644 --- a/api/src/test/resources/io/kaoto/backend/api/resource/eip.kamelet.yaml +++ b/api/src/test/resources/io/kaoto/backend/api/resource/eip.kamelet.yaml @@ -190,5 +190,26 @@ spec: simple: ola ke ase - sort: comparator: myComparator + - do-try: + steps: + - set-body: + simple: abc + - set-exchange-pattern: InOut + do-catch: + - exception: + - java.io.FileNotFoundException + - java.io.IOException + on-when: + simple: ${body.size()} == 1 + steps: + - log: + message: test + logging-level: INFO + log-name: yaml + do-finally: + steps: + - enrich: + expression: + simple: ${body} - to: uri: kamelet:sink diff --git a/kamelet-support/src/main/java/io/kaoto/backend/KamelPopulator.java b/kamelet-support/src/main/java/io/kaoto/backend/KamelPopulator.java index 3494c409e..a9c4de951 100644 --- a/kamelet-support/src/main/java/io/kaoto/backend/KamelPopulator.java +++ b/kamelet-support/src/main/java/io/kaoto/backend/KamelPopulator.java @@ -52,6 +52,7 @@ import io.kaoto.backend.model.deployment.kamelet.step.ToFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.TransactedFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.TransformFlowStep; +import io.kaoto.backend.model.deployment.kamelet.step.TryCatchFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.UnmarshalFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.UriFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.ValidateFlowStep; @@ -397,6 +398,9 @@ private FlowStep processStep(final Step step, final boolean to) { case "choice": flowStep = new ChoiceFlowStep(step, this); break; + case "do-try": + flowStep = new TryCatchFlowStep(step, this); + break; case "filter": flowStep = new FilterFlowStep(step, this); break; diff --git a/kamelet-support/src/main/java/io/kaoto/backend/api/service/deployment/generator/kamelet/KameletRepresenter.java b/kamelet-support/src/main/java/io/kaoto/backend/api/service/deployment/generator/kamelet/KameletRepresenter.java index 9c7b263fd..2f4d36ac7 100644 --- a/kamelet-support/src/main/java/io/kaoto/backend/api/service/deployment/generator/kamelet/KameletRepresenter.java +++ b/kamelet-support/src/main/java/io/kaoto/backend/api/service/deployment/generator/kamelet/KameletRepresenter.java @@ -54,6 +54,7 @@ import io.kaoto.backend.model.deployment.kamelet.step.ToFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.TransactedFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.TransformFlowStep; +import io.kaoto.backend.model.deployment.kamelet.step.TryCatchFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.UnmarshalFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.UriFlowStep; import io.kaoto.backend.model.deployment.kamelet.step.ValidateFlowStep; @@ -303,6 +304,7 @@ private void addEIP() { ToFlowStep.class, TransactedFlowStep.class, TransformFlowStep.class, + TryCatchFlowStep.class, UnmarshalFlowStep.class, UriFlowStep.class, ValidateFlowStep.class diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreaker.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreaker.java index 108972f7b..fbac1b75f 100644 --- a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreaker.java +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreaker.java @@ -29,7 +29,7 @@ public class CircuitBreaker extends EIPStep { private List steps; @JsonProperty(ON_FALLBACK_LABEL) - private CircuitBreakerOnFallback onFallback; + private GenericFlowWithSteps onFallback; @JsonProperty(RESILIENCE_4_J_CONFIGURATION_LABEL) private Map resilience4jConfiguration; @@ -50,7 +50,7 @@ public CircuitBreaker() { @JsonCreator public CircuitBreaker( final @JsonProperty(STEPS_LABEL) List steps, - final @JsonProperty(ON_FALLBACK_LABEL) CircuitBreakerOnFallback onFallback, + final @JsonProperty(ON_FALLBACK_LABEL) GenericFlowWithSteps onFallback, final @JsonProperty(RESILIENCE_4_J_CONFIGURATION_LABEL) Map resilience4jConfiguration, final @JsonProperty(FAULT_TOLERANCE_CONFIGURATION_LABEL) Map faultToleranceConfiguration, final @JsonProperty(CONFIGURATION_LABEL) String configuration, @@ -72,7 +72,7 @@ public CircuitBreaker(final Step step, final KamelPopulator kameletPopulator) { setSteps(kameletPopulator.processSteps(step.getBranches().get(0))); } if (step.getBranches().size() > 1) { - setOnFallback(new CircuitBreakerOnFallback()); + setOnFallback(new GenericFlowWithSteps()); getOnFallback().setSteps(kameletPopulator.processSteps(step.getBranches().get(1))); } } @@ -169,11 +169,11 @@ public void setSteps(final List steps) { this.steps = steps; } - public CircuitBreakerOnFallback getOnFallback() { + public GenericFlowWithSteps getOnFallback() { return onFallback; } - public void setOnFallback(final CircuitBreakerOnFallback onFallback) { + public void setOnFallback(final GenericFlowWithSteps onFallback) { this.onFallback = onFallback; } diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/DoCatch.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/DoCatch.java new file mode 100644 index 000000000..f3ed42ffc --- /dev/null +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/DoCatch.java @@ -0,0 +1,72 @@ +package io.kaoto.backend.model.deployment.kamelet.step; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.kaoto.backend.KamelPopulator; +import io.kaoto.backend.model.deployment.kamelet.FlowStep; +import io.kaoto.backend.model.deployment.kamelet.expression.Expression; +import io.kaoto.backend.model.step.Branch; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + + +@JsonIgnoreProperties(ignoreUnknown = true) +public class DoCatch implements Serializable { + + private List steps; + private List exceptions; + private Expression onWhen; + + public DoCatch(Branch branch, KamelPopulator kameletPopulator) { + setSteps(kameletPopulator.processSteps(branch)); + var exception = branch.getParameters().stream().filter(p -> p.getId().equalsIgnoreCase("exceptions")).findAny(); + if (exception.isPresent()) { + setExceptions(new LinkedList<>()); + Arrays.stream((Object[]) exception.get().getValue()).forEach(v -> getExceptions().add(String.valueOf(v))); + } + var onW = branch.getParameters().stream().filter(p -> p.getId().equalsIgnoreCase("on-when")).findAny(); + if (onW.isPresent() && onW.get().getValue() instanceof Expression e) { + setOnWhen(e); + } + } + + @JsonCreator + public DoCatch( + final @JsonProperty("steps") List steps, + final @JsonProperty("exception") List exceptions, + final @JsonProperty("on-when") Expression onWhen, + final @JsonProperty("onWhen") Expression onWhen2) { + super(); + setSteps(steps); + setExceptions(exceptions); + setOnWhen(onWhen != null ? onWhen : onWhen2); + } + + public List getExceptions() { + return exceptions; + } + + public void setExceptions(final List exceptions) { + this.exceptions = exceptions; + } + + public List getSteps() { + return steps; + } + + public void setSteps(final List steps) { + this.steps = steps; + } + + public Expression getOnWhen() { + return onWhen; + } + + public void setOnWhen(final Expression onWhen) { + this.onWhen = onWhen; + } +} diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/FlowStepDeserializer.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/FlowStepDeserializer.java index 17b0352e3..592f48660 100644 --- a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/FlowStepDeserializer.java +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/FlowStepDeserializer.java @@ -104,6 +104,8 @@ private Map getFlowSteps() { steps.put("to", ToFlowStep.class); steps.put("transacted", TransactedFlowStep.class); steps.put("transform", TransformFlowStep.class); + steps.put("do-try", TryCatchFlowStep.class); + steps.put("doTry", TryCatchFlowStep.class); steps.put("unmarshal", UnmarshalFlowStep.class); steps.put("uri", UriFlowStep.class); steps.put("validate", ValidateFlowStep.class); diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreakerOnFallback.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/GenericFlowWithSteps.java similarity index 71% rename from kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreakerOnFallback.java rename to kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/GenericFlowWithSteps.java index a1b66fc72..593bc753a 100644 --- a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/CircuitBreakerOnFallback.java +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/GenericFlowWithSteps.java @@ -5,16 +5,13 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import io.kaoto.backend.model.deployment.kamelet.FlowStep; -import java.io.Serial; import java.io.Serializable; import java.util.List; @JsonPropertyOrder({"steps"}) @JsonIgnoreProperties(ignoreUnknown = true) -public class CircuitBreakerOnFallback implements Serializable { - @Serial - private static final long serialVersionUID = 3541785323L; +public class GenericFlowWithSteps implements Serializable { @JsonProperty("steps") private List steps; @@ -23,8 +20,7 @@ public List getSteps() { return steps; } - public void setSteps( - final List steps) { + public void setSteps(final List steps) { this.steps = steps; } } diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatch.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatch.java new file mode 100644 index 000000000..e4927ab86 --- /dev/null +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatch.java @@ -0,0 +1,169 @@ +package io.kaoto.backend.model.deployment.kamelet.step; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.kaoto.backend.KamelPopulator; +import io.kaoto.backend.api.metadata.catalog.StepCatalog; +import io.kaoto.backend.api.service.step.parser.kamelet.KameletStepParserService; +import io.kaoto.backend.model.deployment.kamelet.FlowStep; +import io.kaoto.backend.model.parameter.ArrayParameter; +import io.kaoto.backend.model.parameter.ObjectParameter; +import io.kaoto.backend.model.parameter.Parameter; +import io.kaoto.backend.model.step.Branch; +import io.kaoto.backend.model.step.Step; + +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class TryCatch extends EIPStep { + + public static final String STEPS_LABEL = "steps"; + + public static final String DO_CATCH_LABEL = "do-catch"; + public static final String DO_CATCH_LABEL2 = "doCatch"; + + public static final String DO_FINALLY_LABEL = "do-finally"; + public static final String DO_FINALLY_LABEL2 = "doFinally"; + + private List steps; + private List doCatch; + private GenericFlowWithSteps doFinally; + + public TryCatch() { + super(); + } + + @JsonCreator + public TryCatch( + final @JsonProperty(STEPS_LABEL) List steps, + final @JsonProperty(DO_CATCH_LABEL) List doCatch, + final @JsonProperty(DO_CATCH_LABEL2) List doCatch2, + final @JsonProperty(DO_FINALLY_LABEL) GenericFlowWithSteps doFinally, + final @JsonProperty(DO_FINALLY_LABEL2) GenericFlowWithSteps doFinally2) { + super(); + setSteps(steps); + setDoCatch(doCatch != null ? doCatch : doCatch2); + setDoFinally(doFinally != null ? doFinally : doFinally2); + } + + public TryCatch(final Step step, final KamelPopulator kameletPopulator) { + super(step); + + if (step.getBranches() != null) { + if (!step.getBranches().isEmpty()) { + var stepsBranch = step.getBranches().stream() + .filter(b -> b.getIdentifier().equalsIgnoreCase(STEPS_LABEL)) + .findAny(); + if (stepsBranch.isPresent()) { + setSteps(kameletPopulator.processSteps(stepsBranch.get())); + } + } + this.setDoCatch(new LinkedList<>()); + step.getBranches().stream() + .filter(b -> !b.getIdentifier().equalsIgnoreCase(DO_FINALLY_LABEL) + && !b.getIdentifier().equalsIgnoreCase(STEPS_LABEL)) + .forEach(branch -> this.getDoCatch().add(new DoCatch(branch, kameletPopulator))); + var doFinally = + step.getBranches().stream() + .filter(b -> b.getIdentifier().equalsIgnoreCase(DO_FINALLY_LABEL)) + .findAny(); + if (doFinally.isPresent()) { + this.setDoFinally(new GenericFlowWithSteps()); + this.getDoFinally().setSteps(kameletPopulator.processSteps(doFinally.get())); + } + } + } + + @Override + public Map getRepresenterProperties() { + Map properties = new LinkedHashMap<>(); + properties.put(STEPS_LABEL, this.getSteps()); + List> doC = new LinkedList<>(); + for (var doCatch : this.getDoCatch()) { + Map map = new LinkedHashMap<>(); + map.put("exception", doCatch.getExceptions()); + map.put("on-when", doCatch.getOnWhen()); + map.put(STEPS_LABEL, doCatch.getSteps()); + doC.add(map); + } + properties.put(DO_CATCH_LABEL, doC); + Map doF = new LinkedHashMap<>(); + doF.put(STEPS_LABEL, doFinally.getSteps()); + properties.put(DO_FINALLY_LABEL, doF); + return properties; + } + + @Override + public void processBranches(final Step step, final StepCatalog catalog, + final KameletStepParserService kameletStepParserService) { + step.setBranches(new LinkedList<>()); + var identifier = STEPS_LABEL; + step.getBranches().add(createBranch(identifier, this.getSteps(), kameletStepParserService)); + if (this.getDoCatch() != null) { + for (DoCatch doC : this.getDoCatch()) { + final var branch = createBranch(DO_CATCH_LABEL, doC.getSteps(), kameletStepParserService); + branch.setParameters(new LinkedList<>()); + setExceptions(doC, branch); + setOnWhen(doC, branch); + step.getBranches().add(branch); + } + } + if (this.getDoFinally() != null) { + step.getBranches() + .add(createBranch(DO_FINALLY_LABEL, this.getDoFinally().getSteps(), kameletStepParserService)); + } + } + + private void setOnWhen(final DoCatch doC, final Branch branch) { + final var onWhen = new ObjectParameter(); + onWhen.setValue(doC.getOnWhen()); + onWhen.setTitle("On When"); + onWhen.setId("on-when"); + onWhen.setNullable(true); + branch.getParameters().add(onWhen); + } + + private void setExceptions(final DoCatch doC, final Branch branch) { + final var exceptions = new ArrayParameter(); + exceptions.setValue(doC.getExceptions().toArray()); + exceptions.setTitle("Exceptions"); + exceptions.setId("exceptions"); + exceptions.setNullable(false); + branch.getParameters().add(exceptions); + } + + + @Override + protected void assignAttribute(final Parameter parameter) { + } + + @Override + protected void assignProperty(final Parameter parameter) { + } + + public List getSteps() { + return steps; + } + + public void setSteps(final List steps) { + this.steps = steps; + } + + public List getDoCatch() { + return doCatch; + } + + public void setDoCatch(final List doCatch) { + this.doCatch = doCatch; + } + + public GenericFlowWithSteps getDoFinally() { + return doFinally; + } + + public void setDoFinally(final GenericFlowWithSteps doFinally) { + this.doFinally = doFinally; + } +} diff --git a/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatchFlowStep.java b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatchFlowStep.java new file mode 100644 index 000000000..31beafc5e --- /dev/null +++ b/kamelet-support/src/main/java/io/kaoto/backend/model/deployment/kamelet/step/TryCatchFlowStep.java @@ -0,0 +1,55 @@ +package io.kaoto.backend.model.deployment.kamelet.step; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.kaoto.backend.KamelPopulator; +import io.kaoto.backend.api.metadata.catalog.StepCatalog; +import io.kaoto.backend.api.service.step.parser.kamelet.KameletStepParserService; +import io.kaoto.backend.model.deployment.kamelet.FlowStep; +import io.kaoto.backend.model.step.Step; + +import java.util.Map; + +@JsonDeserialize(using = JsonDeserializer.None.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public class TryCatchFlowStep implements FlowStep { + + public static final String LABEL = "do-try"; + public static final String LABEL2 = "doTry"; + + private TryCatch tryCatch; + + @JsonCreator + public TryCatchFlowStep(final @JsonProperty(LABEL) TryCatch tryCatch, + final @JsonProperty(LABEL2) TryCatch tryCatch2) { + super(); + setTryCatch(tryCatch != null ? tryCatch : tryCatch2); + } + + public TryCatchFlowStep(final Step step, final KamelPopulator kameletPopulator) { + super(); + setTryCatch(new TryCatch(step, kameletPopulator)); + } + + @Override + public Map getRepresenterProperties() { + return Map.of(LABEL, getTryCatch().getRepresenterProperties()); + } + + @Override + public Step getStep(final StepCatalog catalog, final KameletStepParserService kameletStepParserService, + final Boolean start, final Boolean end) { + return getTryCatch().getStep(catalog, LABEL, kameletStepParserService); + } + + public TryCatch getTryCatch() { + return tryCatch; + } + + public void setTryCatch(final TryCatch tryCatch) { + this.tryCatch = tryCatch; + } +} diff --git a/kamelet-support/src/test/resources/io/kaoto/backend/api/service/step/parser/kamelet/eip.kamelet.yaml b/kamelet-support/src/test/resources/io/kaoto/backend/api/service/step/parser/kamelet/eip.kamelet.yaml index ebc4521e0..c32854524 100644 --- a/kamelet-support/src/test/resources/io/kaoto/backend/api/service/step/parser/kamelet/eip.kamelet.yaml +++ b/kamelet-support/src/test/resources/io/kaoto/backend/api/service/step/parser/kamelet/eip.kamelet.yaml @@ -157,9 +157,6 @@ spec: - remove-headers: exclude-pattern: toExclude pattern: toRemove - - enrich: - expression: - simple: ${body} - validate: simple: ${body} == 100 - resequence: @@ -183,10 +180,6 @@ spec: simple: ${header.StockSymbol} aggregation-strategy: myAggregatorStrategy completion-size: 2 - - log: - message: test - logging-level: INFO - log-name: yaml - service-call: blacklist-service-filter: servers: @@ -207,9 +200,6 @@ spec: - filter: simple: '{{?foo}}' steps: - - set-body: - simple: abc - - set-exchange-pattern: InOut - idempotent-consumer: idempotent-repository: myRepo simple: ${header.id} @@ -218,5 +208,26 @@ spec: simple: ola ke ase - sort: comparator: myComparator + - do-try: + steps: + - set-body: + simple: abc + - set-exchange-pattern: InOut + do-catch: + - exception: + - java.io.FileNotFoundException + - java.io.IOException + on-when: + simple: ${body.size()} == 1 + steps: + - log: + message: test + logging-level: INFO + log-name: yaml + do-finally: + steps: + - enrich: + expression: + simple: ${body} - to: uri: kamelet:sink