diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch1/output/src/main/mule/mule-config.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch1/output/src/main/mule/mule-config.xml index d1a7b2600..3587c7b59 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch1/output/src/main/mule/mule-config.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch1/output/src/main/mule/mule-config.xml @@ -42,7 +42,7 @@ - + diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch2/output/src/main/mule/mule-config.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch2/output/src/main/mule/mule-config.xml index c3b553471..e68ac8243 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch2/output/src/main/mule/mule-config.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/batch/batch2/output/src/main/mule/mule-config.xml @@ -77,7 +77,7 @@ - + diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/email/email1/output/src/main/mule/mule-config.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/email/email1/output/src/main/mule/mule-config.xml index 17e527f68..4c65ef86e 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/email/email1/output/src/main/mule/mule-config.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/email/email1/output/src/main/mule/mule-config.xml @@ -3,7 +3,7 @@ - + diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/report/report.json b/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/report/report.json index 97b4b58ee..89b114b31 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/report/report.json +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/report/report.json @@ -78,4 +78,4 @@ "documentationLinks": [] } ] -} +} \ No newline at end of file diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/src/main/mule/template.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/src/main/mule/template.xml index c7fb2825b..204b1d3ba 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/src/main/mule/template.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/policy/client_id_enforcement_policy/output/src/main/mule/template.xml @@ -29,7 +29,7 @@ - + {{/isWsdlEndpoint}} diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/report/report.json b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/report/report.json index 1bbeb33d9..05e1e4d2e 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/report/report.json +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/report/report.json @@ -260,4 +260,4 @@ "documentationLinks": [] } ] -} +} \ No newline at end of file diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/src/main/mule/proxy.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/src/main/mule/proxy.xml index 01ef8e9f5..3600b5e44 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/src/main/mule/proxy.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/http/output/src/main/mule/proxy.xml @@ -5,8 +5,8 @@ - @@ -20,8 +20,8 @@ - @@ -75,7 +75,7 @@ - + #[migration::HttpRequester::httpRequesterHeaders(vars)] diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/report/report.json b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/report/report.json index b19a87dcb..da620d211 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/report/report.json +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/report/report.json @@ -376,4 +376,4 @@ "documentationLinks": [] } ] -} +} \ No newline at end of file diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/src/main/mule/raml-proxy.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/src/main/mule/raml-proxy.xml index 3e37c84a9..0ccd904db 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/src/main/mule/raml-proxy.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/raml/output/src/main/mule/raml-proxy.xml @@ -5,8 +5,8 @@ - @@ -55,7 +55,7 @@ - + @@ -90,7 +90,7 @@ - + #[migration::HttpRequester::httpRequesterHeaders(vars)] diff --git a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/using_domain/output/src/main/mule/mule-config.xml b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/using_domain/output/src/main/mule/mule-config.xml index b19b50a5e..2dce72373 100644 --- a/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/using_domain/output/src/main/mule/mule-config.xml +++ b/mule-migration-tool-e2e-tests/src/test/resources/e2e/proxy/using_domain/output/src/main/mule/mule-config.xml @@ -82,7 +82,7 @@ - + #[migration::HttpRequester::httpRequesterHeaders(vars)] @@ -148,7 +148,7 @@ - + diff --git a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MelGrammar.scala b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MelGrammar.scala index efd061d64..51aa4624b 100644 --- a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MelGrammar.scala +++ b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MelGrammar.scala @@ -22,6 +22,7 @@ class MelGrammar(val input: ParserInput) extends Parser with StringBuilding { private val createIfNode = (condition : MelExpressionNode, ifExpr : MelExpressionNode, elseExpr : MelExpressionNode) => IfNode(ifExpr, condition, elseExpr) private val createPropertyNode = (identifiers: Seq[IdentifierNode]) => PropertyNode(identifiers) private val createContainsNode = (left: MelExpressionNode, right: MelExpressionNode) => ContainsNode(left, right) + private val createEmptyLiteralNode = () => EmptyLiteralNode() private val whiteSpaceChar = CharPredicate(" \f\t") @@ -249,8 +250,12 @@ class MelGrammar(val input: ParserInput) extends Parser with StringBuilding { booleanNode | numberNode | values } + def emptyLiteral: Rule1[EmptyLiteralNode] = rule { + ws ~ str("empty") ~> createEmptyLiteralNode + } + def values = rule { - (property | constructor | methodInvocation | stringNodePreserveEscaping | stringNodeSimple | list | map | varReference | enclosedExpression) ~ zeroOrMore(selector) + (property | constructor | methodInvocation | stringNodePreserveEscaping | stringNodeSimple | list | map | emptyLiteral | varReference | enclosedExpression) ~ zeroOrMore(selector) } def constructor: Rule1[ConstructorNode] = rule { diff --git a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MigrationMetadata.scala b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MigrationMetadata.scala index c57f0fe33..5809392aa 100644 --- a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MigrationMetadata.scala +++ b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/MigrationMetadata.scala @@ -11,3 +11,5 @@ case class DefaultMigrationMetadata(override val children: Seq[MigrationMetadata case class JavaModuleRequired() extends MigrationMetadata case class NonMigratable(reason: String) extends MigrationMetadata + +case class MigratableWithWarning(warning: String) extends MigrationMetadata diff --git a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/Migrator.scala b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/Migrator.scala index 99dfa7dfa..7f6e51959 100644 --- a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/Migrator.scala +++ b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/Migrator.scala @@ -8,7 +8,7 @@ import org.mule.weave.v1.parser.Parser import org.mule.weave.v2.{V1OperatorManager, V2LangMigrant} import org.mule.weave.v2.grammar._ import org.mule.weave.v2.parser.ast.logical.{AndNode, OrNode} -import org.mule.weave.v2.parser.annotation.{EnclosedMarkAnnotation, InfixNotationFunctionCallAnnotation, QuotedStringAnnotation} +import org.mule.weave.v2.parser.annotation.{BooleanNotTypeAnnotation, EnclosedMarkAnnotation, InfixNotationFunctionCallAnnotation, NotType, QuotedStringAnnotation} import org.mule.weave.v2.parser.ast.functions.FunctionCallParametersNode import org.mule.weave.v2.parser.ast.header.HeaderNode import org.mule.weave.v2.parser.ast.header.directives.{VersionDirective, VersionMajor, VersionMinor} @@ -26,7 +26,7 @@ object Migrator { val DEFAULT_HEADER = HeaderNode(Seq(VersionDirective(VersionMajor("2"), VersionMinor("0")))) val CLASS_PROPERTY_NAME = "class" - def bindingContextVariable: List[String] = List("message", "exception", "payload", "flowVars", "sessionVars", "recordVars", "null"); + def bindingContextVariable: List[String] = List("message", "exception", "payload", "flowVars", "sessionVars", "recordVars", "null", "empty"); var counter = 0 @@ -47,9 +47,14 @@ object Migrator { case mel.MethodInvocationNode(canonicalName, arguments) => toDataweaveMethodInvocation(canonicalName, arguments) case mel.PropertyNode(name) => toDataweaveProperty(name.map(_.literal).mkString(".")) case mel.ContainsNode(left, right) => toContainsInvocation(left, right) + case mel.EmptyLiteralNode() => toEmptyLiteral() } } + def toEmptyLiteral() = { + new MigrationResult(toDataweaveNameIdentifierNode("empty").dwAstNode, DefaultMigrationMetadata(Seq(MigratableWithWarning("expressions.emptyLiteral")))) + } + def toContainsInvocation(left: MelExpressionNode, right: MelExpressionNode): MigrationResult = { val lRes = toDataweaveAst(left) val rRes = toDataweaveAst(right) @@ -191,8 +196,8 @@ object Migrator { case mel.OperatorType.dot => toDataweaveBinaryOpNode(ValueSelectorOpId, left, right) case mel.OperatorType.subscript => toDataweaveBinaryOpNode(DynamicSelectorOpId, left, right) case mel.OperatorType.plus => toDataweaveBinaryOpNode(AdditionOpId, left, right) - case mel.OperatorType.equals => toDataweaveBinaryOpNode(EqOpId, left, right) - case mel.OperatorType.notEquals => toDataweaveBinaryOpNode(NotEqOpId, left, right) + case mel.OperatorType.equals => toDataweaveEqualityNode(EqOpId, left, right) + case mel.OperatorType.notEquals => toDataweaveEqualityNode(NotEqOpId, left, right) case mel.OperatorType.lessThanOrEqual => toDataweaveBinaryOpNode(LessOrEqualThanOpId, left, right) case mel.OperatorType.greaterThanOrEqual => toDataweaveBinaryOpNode(GreaterOrEqualThanOpId, left, right) case mel.OperatorType.lessThan => toDataweaveBinaryOpNode(LessThanOpId, left, right) @@ -226,6 +231,32 @@ object Migrator { new MigrationResult(AndNode(lRes.dwAstNode, rRes.dwAstNode), DefaultMigrationMetadata(lRes.metadata.children ++ rRes.metadata.children)) } + private def toDataweaveEqualityNode(opId: BinaryOpIdentifier, left: MelExpressionNode, right: MelExpressionNode): MigrationResult = { + val lRes = toDataweaveAst(left) + val rRes = toDataweaveAst(right) + + val nonEmptyRes = if (left.isInstanceOf[EmptyLiteralNode]) { + Some(rRes) + } else if (right.isInstanceOf[EmptyLiteralNode]) { + Some(lRes) + } else { + None + } + + if (nonEmptyRes.isDefined) { + val param = FunctionCallParametersNode(Seq(nonEmptyRes.get.dwAstNode)) + val variableReferenceNode = VariableReferenceNode(NameIdentifier("isEmpty")) + val node = if (opId == NotEqOpId) { + dw.operators.UnaryOpNode(NotOpId, dw.functions.FunctionCallNode(variableReferenceNode, param)).annotate(BooleanNotTypeAnnotation(NotType.Exclamation)) + } else { + dw.functions.FunctionCallNode(variableReferenceNode, param) + } + new MigrationResult(node, DefaultMigrationMetadata(lRes.metadata.children ++ rRes.metadata.children)) + } else { + new MigrationResult(dw.operators.BinaryOpNode(opId, lRes.dwAstNode, rRes.dwAstNode), DefaultMigrationMetadata(lRes.metadata.children ++ rRes.metadata.children)) + } + } + private def toDataweaveBinaryOpNode(opId: BinaryOpIdentifier, left: MelExpressionNode, right: MelExpressionNode, metadata: MigrationMetadata = Empty()): MigrationResult = { val lRes = toDataweaveAst(left) val rRes = toDataweaveAst(right) diff --git a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/ast/MelExpressionNode.scala b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/ast/MelExpressionNode.scala index a0349c6b1..e74fe3899 100644 --- a/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/ast/MelExpressionNode.scala +++ b/mule-migration-tool-expression/src/main/scala/com/mulesoft/tools/ast/MelExpressionNode.scala @@ -57,6 +57,9 @@ case class ContainsNode(left: MelExpressionNode, right: MelExpressionNode) exten case class DWFunctionNode(script: StringNode) extends MelExpressionNode { } +case class EmptyLiteralNode() extends MelExpressionNode { +} + object OperatorType { val plus = 0 val minus = 1 diff --git a/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.ast b/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.ast new file mode 100644 index 000000000..4921f1c7b --- /dev/null +++ b/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.ast @@ -0,0 +1 @@ +|EmptyLiteralNode \ No newline at end of file diff --git a/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.mel b/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.mel new file mode 100644 index 000000000..7b4d68d70 --- /dev/null +++ b/mule-migration-tool-expression/src/test/resources/com/mulesoft/tools/ast/emptyLiteral.mel @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/mule-migration-tool-expression/src/test/scala/com/mulesoft/tools/MigratorTest.scala b/mule-migration-tool-expression/src/test/scala/com/mulesoft/tools/MigratorTest.scala index 71d688723..c81464c5d 100644 --- a/mule-migration-tool-expression/src/test/scala/com/mulesoft/tools/MigratorTest.scala +++ b/mule-migration-tool-expression/src/test/scala/com/mulesoft/tools/MigratorTest.scala @@ -195,4 +195,16 @@ class MigratorTest extends FlatSpec with Matchers { it should "migrate mel expression with isCausedBy function" in { Migrator.migrate("exception.causedBy(org.mule.RuntimeException)").getGeneratedCode() shouldBe "%dw 2.0\n---\nJava::isCausedBy(error.cause, 'org.mule.RuntimeException', false)" } + + it should "migrate empty literal and generate warning" in { + val migrated = Migrator.migrate("payload == empty") + migrated.getGeneratedCode() shouldBe "%dw 2.0\n---\nisEmpty(payload)" + migrated.metadata.children.head shouldBe MigratableWithWarning("expressions.emptyLiteral") + } + + it should "migrate empty literal inequality and generate warning" in { + val migrated = Migrator.migrate("payload != empty") + migrated.getGeneratedCode() shouldBe "%dw 2.0\n---\n!isEmpty(payload)" + migrated.metadata.children.head shouldBe MigratableWithWarning("expressions.emptyLiteral") + } } diff --git a/mule-migration-tool-library/src/main/java/com/mulesoft/tools/migration/library/tools/MelToDwExpressionMigrator.java b/mule-migration-tool-library/src/main/java/com/mulesoft/tools/migration/library/tools/MelToDwExpressionMigrator.java index 42d34e14f..b891e2812 100644 --- a/mule-migration-tool-library/src/main/java/com/mulesoft/tools/migration/library/tools/MelToDwExpressionMigrator.java +++ b/mule-migration-tool-library/src/main/java/com/mulesoft/tools/migration/library/tools/MelToDwExpressionMigrator.java @@ -110,6 +110,16 @@ public String translateSingleExpression(String unwrappedExpression, boolean data return new DefaultMelCompatibilityResolver().resolve(unwrappedExpression, element, report, model, this, enricher); } + if (result.metadata().children().exists(a -> a instanceof MigratableWithWarning)) { + List metadata = + (List) (List) JavaConverters.seqAsJavaList(result.metadata().children()) + .stream() + .filter(a -> a instanceof MigratableWithWarning) + .collect(toList()); + + metadata.forEach(a -> report.report(a.warning(), element, element)); + } + if (migratedExpression.contains("message.inboundAttachments")) { report.report("message.expressionsAttachments", element, element); } diff --git a/mule-migration-tool-library/src/main/resources/report.yaml b/mule-migration-tool-library/src/main/resources/report.yaml index 5bff9b1df..dc1ca290b 100644 --- a/mule-migration-tool-library/src/main/resources/report.yaml +++ b/mule-migration-tool-library/src/main/resources/report.yaml @@ -90,19 +90,25 @@ expressions: type: ERROR message: "Cannot migrate 'encodeBase64' method. Consider using the 'toBase64' DataWeave function." docLinks: - - https://docs.mulesoft.com/mule-runtime/4.3/dw-binaries-functions-tobase64 + - https://docs.mulesoft.com/dataweave/2.3/dw-binaries-functions-tobase64 datamapper: type: ERROR message: DataMapper migration is not supported. Use the DataWeave Migrator tool to migrate uses of DataMapper. docLinks: - - https://docs.mulesoft.com/mule-user-guide/v/3.8/dataweave-migrator + - https://docs.mulesoft.com/dataweave/1.2/dataweave-migrator - https://beta.docs.stgx.mulesoft.com/beta-mule-migration-tool/mule-runtime/4.3/migration-tool-procedure#datamapper methodInvocation: type: WARN message: The MEL expression contains a method invocation that could not be migrated to a Dataweave expression. docLinks: - - https://docs.mulesoft.com/mule-runtime/4.3/dataweave-cookbook-java-methods + - https://docs.mulesoft.com/dataweave/2.4/dataweave-cookbook-java-methods - https://docs.mulesoft.com/mule-runtime/4.3/migration-mel + emptyLiteral: + type: WARN + message: "DataWeave 'isEmpty' function yields different results than MEL '== empty' comparison when tested against 0, strings with only whitespace and false." + docLinks: + - https://docs.mulesoft.com/dataweave/2.3/dw-core-functions-isempty + dataWeave: migrationErrorFile: type: ERROR @@ -158,7 +164,7 @@ http: message: "A 'parseRequest' is not needed in Mule 4 because the 'InputStream' of the multipart payload is provided as it is read." docLinks: - https://docs.mulesoft.com/mule-runtime/4.3/migration-connectors-http#http-mime-types - - https://docs.mulesoft.com/mule-runtime/4.3/dataweave-formats#format_form_data + - https://docs.mulesoft.com/dataweave/2.4/dataweave-formats statusCode: type: WARN message: Avoid using an outbound property to determine the status code. @@ -190,7 +196,7 @@ http: message: "'parseResponse' is not needed in Mule 4 because DataWeave 2.0 now handles those MIME types." docLinks: - https://docs.mulesoft.com/mule-runtime/4.3/migration-connectors-http#http-mime-types - - https://docs.mulesoft.com/mule-runtime/4.3/dataweave-formats#format_form_data + - https://docs.mulesoft.com/dataweave/2.4/dataweave-formats socketProperties: type: ERROR message: The server socket properties can be configured at the runtime level in Mule 4.2.0 or higher. @@ -369,7 +375,7 @@ db: type: ERROR message: Add a suitable JDBC driver dependency for this connection. docLinks: - - https://docs.mulesoft.com/db-connector/1.8/database-connector-examples#configure-a-database-connection + - https://docs.mulesoft.com/db-connector/1.8/database-connector-connection jdbcUrlForSpecificEngine: type: INFO message: "The config in Mule 3 is specific for an engine, but it contained a 'url' attribute. It will be made generic to keep the URL." @@ -394,7 +400,7 @@ email: type: INFO message: Email transformers are no longer needed. The payload already has the type information. docLinks: - - https://docs.mulesoft.com/mule-runtime/4.3/intro-transformations#data-types-and-object-to-string-byte-inputstream-transformers + - https://docs.mulesoft.com/mule-runtime/4.3/intro-transformations#data-types-and-object-to-stringbyteinputstream-transformers moveToFolder: type: ERROR message: "'moveToFolder' is not yet supported in Email Connector." @@ -758,7 +764,7 @@ oauth2Provider: message: "You can no longer add a custom implementation from a spring bean. Only Period Rate Limiter is implemented for now." docLinks: - https://docs.mulesoft.com/mule-runtime/4.3/migration-oauth2-provider - - https://docs.mulesoft.com/oauth2-provider-module/1.0/#client-validation-rate-limiter + - https://docs.mulesoft.com/oauth2-provider-module/1.0/#PeriodRateLimiter validateClient: type: ERROR message: "'oauth2-provider:validate-client' is not supported in Mule 4." @@ -930,7 +936,7 @@ mulexml: type: ERROR message: "Conversion to Java object is no longer needed on Mule 4. You can access the data using DW." docLinks: - - https://beta.docs.stgx.mulesoft.com/beta-mule-migration-tool/mule-runtime/4.3/migration-transformers#object-model-representation-for-json-and-xml + - https://docs.mulesoft.com/mule-runtime/4.3/migration-transformers#object-model-representation-for-json-and-xml configuration: processingStrategy: