From 79e08c85144fe9b08ee0d04dc796b2d134be870f Mon Sep 17 00:00:00 2001 From: baubakg Date: Tue, 10 Sep 2024 22:58:33 +0200 Subject: [PATCH 1/6] Fixed #160 : implementing plugins to extraction of objects --- README.md | 23 +- .../testdata/one/MimeMessageMethods.java | 23 +- integroBridgeService/pom.xml | 5 + .../bridge/plugins/IBSDeserializerPlugin.java | 28 ++ .../bridge/service/BridgeServiceFactory.java | 2 + .../tests/bridge/service/CallContent.java | 3 + .../bridge/service/ConfigValueHandlerIBS.java | 4 +- .../bridge/service/IBSPluginManager.java | 72 +++++ .../tests/bridge/service/IntegroAPI.java | 2 + .../tests/bridge/service/JavaCallResults.java | 4 +- .../tests/bridge/service/MetaUtils.java | 6 + .../MimeExtractionPluginDeserializer.java | 74 +++++ .../tests/bridge/service/E2ETests.java | 1 - .../tests/bridge/service/MetaUtilsTests.java | 292 ++++++++---------- 14 files changed, 367 insertions(+), 172 deletions(-) create mode 100644 integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/plugins/IBSDeserializerPlugin.java create mode 100644 integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/plugins/deserializer/MimeExtractionPluginDeserializer.java diff --git a/README.md b/README.md index 40dcc3ca..d73846d8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ from any language or framework you are in. ## Table of Contents +* [BridgeService](#bridgeservice) + * [Table of Contents](#table-of-contents) * [Background](#background) * [Demo Project](#demo-project) * [Release Notes](#release-notes) @@ -36,6 +38,8 @@ from any language or framework you are in. * [Lists and Arrays](#lists-and-arrays) * [Complex Types](#complex-types) * [Files](#files) + * [Results](#results) + * [Deserialization Plugins](#deserialization-plugins) * [Managing Timeouts](#managing-timeouts) * [Setting Timeout Globally](#setting-timeout-globally) * [Setting a Timeout for the Call Session](#setting-a-timeout-for-the-call-session) @@ -49,7 +53,7 @@ from any language or framework you are in. * [Making Assertions](#making-assertions) * [Duration-Based Assertions](#duration-based-assertions) * [Error Management](#error-management) - * [Contribution](#contribution) + * [Contributing to the Project](#contributing-to-the-project) * [Known Errors](#known-errors) * [Linked Error](#linked-error) * [Known Issues and Limitations](#known-issues-and-limitations) @@ -495,6 +499,21 @@ The result is then: } ``` +## Results +Results are returned as a JSON Object. Serializable return objects are deserialized. For objects that are not Serializable, we perform an operatio called scraping, which involves sequentially calling the simple getters of the object, and include the results in the result object. + +In the case of complex classes where the scraping is not sufficient, you can define a deserialization plugin for that class. This allow you to be specific regarding how the object can be returned. For mor information on this you can refer to the chapter [Deserialization Plugins](#deserialization-plugins). + +### Deserialization Plugins +As of version 2.11.17, we introduced the notion of plugins. For now you can customize how an object is deserialized. This can be usefull when the default object serialization is incomplete or not to your liking. + +To create your plugin you need to: +* Implement the interface methods of the interface `com.adobe.campaign.tests.bridge.service.plugins.IBSDeserializerPlugin`. +* Add the package of the plugin to the environment variable `IBS.DESERIALIZATION.PLUGINS`. + +There is an example of the plugin in the tests under `integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/plugins/deserializer/MimeExtractionPluginDeserializer.java`. + + ## Managing Timeouts As of version 2.11.6 we now introduce the notion of timeouts. This means that after a declared time a call will be @@ -873,7 +892,7 @@ The BridgeService exception is how the bridgeService manages underlying errors. * The step at which the error occured * The stack trace of the originating exception -## Contribution +## Contributing to the Project There are two main docs for contributing: diff --git a/bridgeService-data/src/main/java/com/adobe/campaign/tests/bridge/testdata/one/MimeMessageMethods.java b/bridgeService-data/src/main/java/com/adobe/campaign/tests/bridge/testdata/one/MimeMessageMethods.java index a4fc4927..d523f67e 100644 --- a/bridgeService-data/src/main/java/com/adobe/campaign/tests/bridge/testdata/one/MimeMessageMethods.java +++ b/bridgeService-data/src/main/java/com/adobe/campaign/tests/bridge/testdata/one/MimeMessageMethods.java @@ -26,7 +26,7 @@ public static MimeMessage createMessage(String in_suffix) throws MessagingExcept message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress("a@b.com")); message.setSubject("a subject by me " + in_suffix); - message.setText("a content by yours truely " + in_suffix); + message.setText("a content by yours truly " + in_suffix); return message; } @@ -115,6 +115,8 @@ public static MimeMessage createMultiPartAlternativeMessage(String in_suffix) th // Step 2: Create a default MimeMessage object MimeMessage message = new MimeMessage(session); + message.setSentDate(new java.util.Date()); + // Step 3: Set From, To, Subject message.setFrom(new InternetAddress("from@example.com")); message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com")); @@ -138,5 +140,24 @@ public static MimeMessage createMultiPartAlternativeMessage(String in_suffix) th message.setContent(multipart); return message; } + + public static Multipart createMutliPartHTML() throws MessagingException { + + Multipart multipart = new MimeMultipart(); + + // PLAIN TEXT + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setText("Here is your plain text message"); + multipart.addBodyPart(messageBodyPart); + + // HTML TEXT + // HTML TEXT + BodyPart messageBodyPart2 = new MimeBodyPart(); + String htmlText = "

I am the html part

"; + messageBodyPart2.setContent(htmlText, "text/html"); + multipart.addBodyPart(messageBodyPart2); + + return multipart; + } } diff --git a/integroBridgeService/pom.xml b/integroBridgeService/pom.xml index 2520f3e3..40231c4b 100644 --- a/integroBridgeService/pom.xml +++ b/integroBridgeService/pom.xml @@ -162,6 +162,11 @@ 5.12.0 test + + org.reflections + reflections + 0.9.12 + diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/plugins/IBSDeserializerPlugin.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/plugins/IBSDeserializerPlugin.java new file mode 100644 index 00000000..8ddb4b22 --- /dev/null +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/plugins/IBSDeserializerPlugin.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.plugins; + +import java.util.Map; + +public interface IBSDeserializerPlugin { + /** + * Lets us know if the given Object applies to this Plugin + * @param in_object an object we want to deserialize + * @return true if we can use this plugin for the given Object + */ + public boolean appliesTo(Object in_object); + + /** + * Applies the plugin to the given object + * @param in_object an object we want to deserialize + * @return A Map of data extracted from the object + */ + public Map apply(Object in_object); +} + diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/BridgeServiceFactory.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/BridgeServiceFactory.java index 81735084..d2da4245 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/BridgeServiceFactory.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/BridgeServiceFactory.java @@ -21,6 +21,7 @@ public class BridgeServiceFactory { * Creates a Java Call Object given a JSON as a String * @param in_requestJSON A JSON Object as a String * @return A Java Call Object + * @throws JsonProcessingException thrown when the JSON return object could not be created */ public static JavaCalls createJavaCalls(String in_requestJSON) throws JsonProcessingException { LogManagement.logStep(LogManagement.STD_STEPS.ANALYZING_PAYLOAD); @@ -54,6 +55,7 @@ public static String transformJavaCallResultsToJSON(JavaCallResults in_callResul * Creates a ServiceAccess Object given a JSON as a String * @param in_requestJSON A JSON Object as a String * @return A Service Access Object + * @throws JsonProcessingException thrown when the JSON return object could not be created */ public static ServiceAccess createServiceAccess(String in_requestJSON) throws JsonProcessingException { LogManagement.logStep(LogManagement.STD_STEPS.ANALYZING_PAYLOAD); diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/CallContent.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/CallContent.java index 8dd0576c..ea73c3f1 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/CallContent.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/CallContent.java @@ -72,6 +72,7 @@ public void setArgs(Object[] args) { /** * Returns the method object that is defined by this class * + * @param in_class Class for which we want to fetch the method from * @return the method object */ public Method fetchMethod(Class in_class) { @@ -87,6 +88,7 @@ public Method fetchMethod(Class in_class) { /** * Returns the method object that is defined by this class * + * @param in_class the class we want to instantiate * @return the method object */ public Constructor fetchConstructor(Class in_class) { @@ -136,6 +138,7 @@ protected Method fetchMethod() throws ClassNotFoundException { /** * Calls the java method defined in this class * + * @param iClassLoader The class loader used for loading and executing the class and method. * @return the value of this call */ public Object call(IntegroBridgeClassLoader iClassLoader) { diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java index cc040889..99041372 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java @@ -64,7 +64,9 @@ public void activate(String in_value) { SECRETS_BLOCK_OUTPUT("IBS.SECRETS.BLOCK.OUTPUT", "true", false, "When set to true, we forbid the system from returning the value in the return payload. This is to avoid XSS attacks. This should ONLY be true when you want to debug."), DESERIALIZATION_DEPTH_LIMIT("IBS.DESERIALIZATION.DEPTH.LIMIT", "1", false, - "This value sets the maximum depth of the deserialization."); + "This value sets the maximum depth of the deserialization."), + PLUGIN_DESERIALIZATION_PATH( + "IBS.PLUGINS.PATH", null, false, "The package path to look for the plugins."); public final String systemName; public final String defaultValue; diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java new file mode 100644 index 00000000..1679157b --- /dev/null +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; +import org.reflections.Reflections; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +public class IBSPluginManager { + + public static void loadPlugins() { + ExtractionPlugins.loadPlugins(); + } + + static class ExtractionPlugins { + + static Set plugins = new LinkedHashSet<>(); + + static Map apply(Object in_object) { + return plugins.stream().filter(a -> a.appliesTo(in_object)).findFirst().get().apply(in_object); + } + + static void clearPlugins() { + plugins = new LinkedHashSet<>(); + } + + /** + * Checks if there is a plugin that applies to the given object + * @param in_object an arbitrary object + * @return true if there is a plugin that applies to the given + */ + static boolean appliesTo(Object in_object) { + return plugins.stream().anyMatch(a -> a.appliesTo(in_object)); + } + + public static void loadPlugins() { + if (ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.isSet()) { + Reflections reflections = new Reflections(ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.fetchValue()); + Set> classes = reflections.getSubTypesOf(IBSDeserializerPlugin.class); + + for (Class implementingClass : classes) { + try { + Constructor ctor = implementingClass.getConstructor(); + + plugins.add( (IBSDeserializerPlugin) ctor.newInstance()); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + } + } + } + +} diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IntegroAPI.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IntegroAPI.java index 2e09d2cb..5be74f4f 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IntegroAPI.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IntegroAPI.java @@ -56,6 +56,8 @@ public static void startServices(int port) { throw new IBSConfigurationException("The port " + port + " is not currently free."); } + IBSPluginManager.loadPlugins(); + if (Boolean.parseBoolean(ConfigValueHandlerIBS.SSL_ACTIVE.fetchValue())) { File l_file = new File(ConfigValueHandlerIBS.SSL_KEYSTORE_PATH.fetchValue()); if (!l_file.exists()) { diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/JavaCallResults.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/JavaCallResults.java index a80a2d38..0965986b 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/JavaCallResults.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/JavaCallResults.java @@ -61,10 +61,10 @@ public void addResult(String in_key, Object callResult, long in_callDuration) { } /** - * If the given object is a key, we return the duration stored for that key. Otherwise we return the + * Used for assertions where we assert the duration of the executions. If the given object is a key, we return the duration stored for that key. Otherwise we return the exact 'long' value of the given duration. * * @param in_keyOrValue An object that is either a key or an - * @return + * @return A duration in milliseconds */ public Long expandDurations(Object in_keyOrValue) { if (getCallDurations().containsKey(in_keyOrValue) ) { diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/MetaUtils.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/MetaUtils.java index 2128a006..59eba2b5 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/MetaUtils.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/MetaUtils.java @@ -97,6 +97,12 @@ public static boolean isExtractable(Method in_method) { * @return A Map of serialized Objects */ public static Object extractValuesFromObject(Object in_object) { + //Check if there is a plugin for this object + + if (IBSPluginManager.ExtractionPlugins.appliesTo(in_object)) { + return IBSPluginManager.ExtractionPlugins.apply(in_object); + } + return extractValuesFromObject(in_object, 0); } diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/plugins/deserializer/MimeExtractionPluginDeserializer.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/plugins/deserializer/MimeExtractionPluginDeserializer.java new file mode 100644 index 00000000..8c981e31 --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/plugins/deserializer/MimeExtractionPluginDeserializer.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.plugins.deserializer; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; + +import java.io.IOException; +import java.util.*; + +import javax.mail.*; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +public class MimeExtractionPluginDeserializer implements IBSDeserializerPlugin { + + @Override + public boolean appliesTo(Object in_object) { + if (in_object instanceof MimeMessage) { + return true; + } + + if (in_object instanceof Message) { + return true; + } + + return false; + } + + @Override + public Map apply(Object in_object) { + MimeMessage l_message = (MimeMessage) in_object; + Map l_returnMap = new HashMap<>(); + try { + l_returnMap.put("subject", l_message.getSubject()); + l_returnMap.put("from", l_message.getFrom()); + l_returnMap.put("recipients", l_message.getAllRecipients()); + + if (l_message.getContent() instanceof MimeMultipart) { + MimeMultipart l_multipart = (MimeMultipart) l_message.getContent(); + List> l_parts = new ArrayList<>(); + for (int p = 0; p < l_multipart.getCount(); p++) { + BodyPart l_bodyPart = l_multipart.getBodyPart(p); + Map l_partMap = new HashMap<>(); + l_partMap.put("contentType", l_bodyPart.getContentType()); + l_partMap.put("description", l_bodyPart.getDescription()); + l_partMap.put("content", l_bodyPart.getContent()); + l_parts.add(l_partMap); + } + l_returnMap.put("content", l_parts); + } else { + l_returnMap.put("content", l_message.getContent()); + } + l_returnMap.put("contentType", l_message.getContentType()); + l_returnMap.put("description", l_message.getDescription()); + l_returnMap.put("receivedDate", l_message.getReceivedDate()); + l_returnMap.put("sentDate", l_message.getSentDate()); + l_returnMap.put("size", l_message.getSize()); + l_returnMap.put("flags", l_message.getFlags()); + l_returnMap.put("messageNumber", l_message.getMessageNumber()); + l_returnMap.put("lineCount", l_message.getLineCount()); + } catch (MessagingException | IOException e) { + throw new RuntimeException(e); + } + return l_returnMap; + } + + +} diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/E2ETests.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/E2ETests.java index 7b58cd36..c27499fa 100644 --- a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/E2ETests.java +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/E2ETests.java @@ -591,7 +591,6 @@ public void test_issue159_callToAlternativeMultipartMimeMessage() { var response = given().body(jc).post(EndPointURL + "call"); System.out.println(response.thenReturn().getBody().asPrettyString()); response.then().assertThat().statusCode(200); - } diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java index 8e6a66aa..1fa3293c 100644 --- a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java @@ -8,6 +8,8 @@ */ package com.adobe.campaign.tests.bridge.service; +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; +import com.adobe.campaign.tests.bridge.plugins.deserializer.MimeExtractionPluginDeserializer; import com.adobe.campaign.tests.bridge.testdata.nested.NestedExampleA_Level1; import com.adobe.campaign.tests.bridge.testdata.nested.NestedExampleA_Level2; import com.adobe.campaign.tests.bridge.testdata.nested.NestedExampleA_Level3; @@ -22,13 +24,8 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import javax.activation.DataHandler; import javax.mail.*; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.util.ByteArrayDataSource; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Method; @@ -41,6 +38,7 @@ public class MetaUtilsTests { @AfterClass public void setUp() { ConfigValueHandlerIBS.resetAllValues(); + IBSPluginManager.ExtractionPlugins.clearPlugins(); } //Tests for extracting data from an object @@ -139,169 +137,12 @@ public void prepareExtractMimeMessage() } - public static MimeMessage createMultiPartMessage(String in_suffix) throws MessagingException { - String from = "qa_sender@qamail.rd.campaign.adobe.com"; - MimeMessage message = new MimeMessage((Session) null); - message.setFrom(new InternetAddress(from)); - message.addRecipient(Message.RecipientType.TO, new InternetAddress("a@b.com")); - message.setSubject("a subject by me " + in_suffix); - - // Create a multipart message - MimeMultipart multipart = new MimeMultipart("example"); - - // Part one is the text - MimeBodyPart textPart = new MimeBodyPart(); - textPart.setText("Part 1 : a content by yours truely " + in_suffix,"utf-8"); - - // Part two is an attachment - //MimeBodyPart attachmentPart = new MimeBodyPart(); - //attachmentPart.setText("Part 2 : a content by yours truely " + in_suffix); - - MimeBodyPart attachmentPart = new MimeBodyPart(); - ByteArrayDataSource ds = new ByteArrayDataSource("attachment content".getBytes(), "text/plain"); - attachmentPart.setDataHandler(new DataHandler(ds)); - attachmentPart.setFileName("testaRosa.txt"); - - - // Add parts to the multipart - multipart.addBodyPart(textPart); - multipart.addBodyPart(attachmentPart); - - // Set the multipart as the message's content - message.setContent(multipart); - return message; - } - - public static MimeMessage createMultiPartAlternativeMessage(String in_suffix) throws MessagingException { - // Step 1: Establish a mail session - Properties properties = System.getProperties(); - Session session = Session.getDefaultInstance(properties); - - // Step 2: Create a default MimeMessage object - MimeMessage message = new MimeMessage(session); - - // Step 3: Set From, To, Subject - message.setFrom(new InternetAddress("from@example.com")); - message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com")); - message.setSubject("This is the Subject Line!"); - - // Step 4: Create a multipart/alternative content - Multipart multipart = new MimeMultipart("alternative"); - - // Step 5: Create two body parts - BodyPart textBodyPart = new MimeBodyPart(); - textBodyPart.setText("This is the text version of the email."); - - BodyPart htmlBodyPart = new MimeBodyPart(); - htmlBodyPart.setContent("

This is the HTML version of the email.

", "text/html"); - - // Step 6: Add the body parts to the multipart - multipart.addBodyPart(textBodyPart); - multipart.addBodyPart(htmlBodyPart); - - // Step 7: Set the multipart object to the message content - message.setContent(multipart); - return message; - } - - Message createMPMHTML() throws MessagingException { - /* - Properties props = new Properties(); - - props.put("mail.transport.protocol", "smtp"); - props.put("mail.smtp.host", "MYMAILSERVER"); - props.setProperty("mail.smtp.auth", "true"); - final PasswordAuthentication auth = new PasswordAuthentication(smtpUser, stmpPassword); - Session mailSession = Session.getDefaultInstance(props, new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { return auth; } - }); - Session session = Session.getInstance(props,null); - MimeMessage message = new MimeMessage(session); - */ - /* - Properties props = new Properties(); - props.put("mail.smtps.host", "smtp.gmail.com"); - props.put("mail.smtps.auth", "true"); - - props.put("mail.smtps.port", 123); - - // Get the Session object. - Session session = Session.getInstance(props, null); - - MimeMessage message = new MimeMessage(session); - - */ - MimeMessage message = new MimeMessage((Session) null); - - InternetAddress from = new InternetAddress("from@me.com"); - InternetAddress to = new InternetAddress("to@you.com"); - - message.setSubject("I am a multipart text/html email" ); - message.setFrom(from); - message.addRecipient(Message.RecipientType.TO, to); - - Multipart multipart = new MimeMultipart(); - - // PLAIN TEXT - BodyPart messageBodyPart = new MimeBodyPart(); - messageBodyPart.setText("Here is your plain text message"); - multipart.addBodyPart(messageBodyPart); - - // HTML TEXT - BodyPart messageBodyPart2 = new MimeBodyPart(); - String htmlText = "

I am the html part

"; - messageBodyPart2.setContent(htmlText, "text/html"); - multipart.addBodyPart(messageBodyPart2); - - message.setContent(multipart); - return message; - } - - BodyPart createMimePart() throws MessagingException { - - Multipart multipart = new MimeMultipart(); - - // PLAIN TEXT - BodyPart messageBodyPart = new MimeBodyPart(); - messageBodyPart.setText("Here is your plain text message"); - multipart.addBodyPart(messageBodyPart); - - // HTML TEXT - // HTML TEXT - BodyPart messageBodyPart2 = new MimeBodyPart(); - String htmlText = "

I am the html part

"; - messageBodyPart2.setContent(htmlText, "text/html"); - multipart.addBodyPart(messageBodyPart2); - - return messageBodyPart2; - } - - Multipart createMutliPart() throws MessagingException { - - Multipart multipart = new MimeMultipart(); - - // PLAIN TEXT - BodyPart messageBodyPart = new MimeBodyPart(); - messageBodyPart.setText("Here is your plain text message"); - multipart.addBodyPart(messageBodyPart); - - // HTML TEXT - // HTML TEXT - BodyPart messageBodyPart2 = new MimeBodyPart(); - String htmlText = "

I am the html part

"; - messageBodyPart2.setContent(htmlText, "text/html"); - multipart.addBodyPart(messageBodyPart2); - - return multipart; - } - @Test public void prepareExtractMultiPartMimeMessage() throws MessagingException, JsonProcessingException { //List m1 = Collections.singletonList(createMultiPartMessage("dddd")); - Object x = createMutliPart(); + Object x = MimeMessageMethods.createMutliPartHTML(); //assertThat("The values should be extractable", MetaUtils.isExtractable(x.getClass())); JavaCallResults jcr = new JavaCallResults(); @@ -412,7 +253,7 @@ public void testDeserializer() assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); assertThat(l_result.get("size"), Matchers.equalTo(-1)); assertThat(l_result.get("subject"), Matchers.equalTo("a subject by me " + l_suffix)); - assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truely " + l_suffix)); + assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); } @@ -436,7 +277,7 @@ public void testDeserializer_collection() assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); assertThat(l_result.get("size"), Matchers.equalTo(-1)); assertThat(l_result.get("subject"), Matchers.equalTo("a subject by me " + l_suffix)); - assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truely " + l_suffix)); + assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); } @@ -518,6 +359,8 @@ public void testNestedControl() throws JsonProcessingException, NoSuchMethodExce String result = BridgeServiceFactory.transformJavaCallResultsToJSON(jcr, new HashSet<>()); assertThat("We should see the effects of nesting", ((Map)((Map) l_result3).get("level2")).get("level3"), Matchers.equalTo("... of type "+NestedExampleA_Level3.class.getName())); + + } @Test @@ -530,5 +373,124 @@ public void testingIsBasic() { assertThat("String is basic", !MetaUtils.isBasicReturnType(MimeMessage.class)); } + /////// Tests for the MultiPartMime plugin + + @Test + public void testMutliPartMimePlugin() throws MessagingException, JsonProcessingException { + var l_suffix = "abc"; + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + //Message l_message = MimeMessageMethods.createMessage(l_suffix); + IBSDeserializerPlugin mimePlugin = new MimeExtractionPluginDeserializer(); + assertThat("Applies to should be a message", mimePlugin.appliesTo(l_message)); + + Map l_result = mimePlugin.apply(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + @Test + public void testThePluginManager() throws MessagingException { + var l_suffix = "def"; + + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + + //add plugin + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + //remove plugin + IBSPluginManager.ExtractionPlugins.clearPlugins(); + assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + } + + @Test + public void testThePluginManagerUsageLevel1() throws MessagingException { + var l_suffix = "def"; + + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + + //add plugin + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + Map l_result = (Map) IBSPluginManager.ExtractionPlugins.apply(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + + @Test + public void testUsageOfTheExtractionPlugIn() throws MessagingException { + var l_suffix = "abc"; + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + assertThat("Since no plugin has been declared content should be generated by default",l_result.get("content"), Matchers.not(Matchers.instanceOf(List.class))); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + + l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + @Test + public void testUsageOfTheExtractionPlugInConfigBased() throws MessagingException { + + var l_suffix = "abc"; + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + assertThat("Since no plugin has been declared content should be generated by default",l_result.get("content"), Matchers.not(Matchers.instanceOf(List.class))); + + ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.activate("com.adobe.campaign.tests.bridge.plugins.deserializer"); + IBSPluginManager.loadPlugins(); + + l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + } From 711249e42afead3197646592083f502c6daa55d0 Mon Sep 17 00:00:00 2001 From: baubakg Date: Wed, 11 Sep 2024 14:53:01 +0200 Subject: [PATCH 2/6] #160 added negative tests. Also refactored the plugin tests --- .../bridge/service/IBSPluginManager.java | 45 ++-- .../tests/bridge/service/MetaUtilsTests.java | 97 --------- .../tests/bridge/service/PluginTests.java | 202 ++++++++++++++++++ .../badctor/BadExtractionPlugin_abstract.java | 28 +++ .../BadExtractionPlugin_badConstructor.java | 29 +++ ...dExtractionPlugin_ctorThrowsException.java | 29 +++ ...BadExtractionPlugin_ctorIllegalAccess.java | 28 +++ 7 files changed, 344 insertions(+), 114 deletions(-) create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_abstract.java create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_badConstructor.java create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_ctorThrowsException.java create mode 100644 integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor2/BadExtractionPlugin_ctorIllegalAccess.java diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java index 1679157b..f55a131f 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java @@ -9,11 +9,11 @@ package com.adobe.campaign.tests.bridge.service; import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; +import com.adobe.campaign.tests.bridge.service.exceptions.IBSConfigurationException; import org.reflections.Reflections; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -38,6 +38,7 @@ static void clearPlugins() { /** * Checks if there is a plugin that applies to the given object + * * @param in_object an arbitrary object * @return true if there is a plugin that applies to the given */ @@ -45,28 +46,38 @@ static boolean appliesTo(Object in_object) { return plugins.stream().anyMatch(a -> a.appliesTo(in_object)); } - public static void loadPlugins() { + static void loadPlugins() { if (ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.isSet()) { - Reflections reflections = new Reflections(ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.fetchValue()); - Set> classes = reflections.getSubTypesOf(IBSDeserializerPlugin.class); + Reflections reflections = new Reflections( + ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.fetchValue()); + Set> classes = reflections.getSubTypesOf( + IBSDeserializerPlugin.class); for (Class implementingClass : classes) { - try { - Constructor ctor = implementingClass.getConstructor(); - - plugins.add( (IBSDeserializerPlugin) ctor.newInstance()); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } + addPlugin(implementingClass); } } } + + static void addPlugin(Class implementingClass) { + try { + Constructor ctor = implementingClass.getConstructor(); + + plugins.add((IBSDeserializerPlugin) ctor.newInstance()); + } catch (InstantiationException e) { + throw new IBSConfigurationException("The given plugin " + implementingClass.getName() + + " cannot be instantiated.", e); + } catch (IllegalAccessException e) { + throw new IBSConfigurationException("We do not have access to the plugin " + implementingClass.getName() + + ". This is probably a scope issue. Please review this and rerun the IBS.", e); + } catch (InvocationTargetException e) { + throw new IBSConfigurationException( + "The constructor of the plugin " + implementingClass.getName() + " threw an exception.", e); + } catch (NoSuchMethodException e) { + throw new IBSConfigurationException( + "The plugin " + implementingClass.getName() + " does not have a default constructor.", e); + } + } } } diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java index 1fa3293c..42eca2c3 100644 --- a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/MetaUtilsTests.java @@ -396,101 +396,4 @@ public void testMutliPartMimePlugin() throws MessagingException, JsonProcessingE assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); } - @Test - public void testThePluginManager() throws MessagingException { - var l_suffix = "def"; - - Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); - - assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); - - MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); - - //add plugin - IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); - assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); - - //remove plugin - IBSPluginManager.ExtractionPlugins.clearPlugins(); - assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); - } - - @Test - public void testThePluginManagerUsageLevel1() throws MessagingException { - var l_suffix = "def"; - - Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); - - MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); - - //add plugin - IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); - assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); - - Map l_result = (Map) IBSPluginManager.ExtractionPlugins.apply(l_message); - - assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("size"), Matchers.equalTo(-1)); - assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); - assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); - var contentList = (List>) l_result.get("content"); - assertThat(contentList.size(), Matchers.equalTo(2)); - assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); - assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); - } - - - @Test - public void testUsageOfTheExtractionPlugIn() throws MessagingException { - var l_suffix = "abc"; - Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); - - Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); - assertThat("Since no plugin has been declared content should be generated by default",l_result.get("content"), Matchers.not(Matchers.instanceOf(List.class))); - - MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); - IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); - - l_result = (Map) MetaUtils.extractValuesFromObject(l_message); - - assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("size"), Matchers.equalTo(-1)); - assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); - //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); - assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); - var contentList = (List>) l_result.get("content"); - assertThat(contentList.size(), Matchers.equalTo(2)); - assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); - assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); - } - - @Test - public void testUsageOfTheExtractionPlugInConfigBased() throws MessagingException { - - var l_suffix = "abc"; - Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); - - Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); - assertThat("Since no plugin has been declared content should be generated by default",l_result.get("content"), Matchers.not(Matchers.instanceOf(List.class))); - - ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.activate("com.adobe.campaign.tests.bridge.plugins.deserializer"); - IBSPluginManager.loadPlugins(); - - l_result = (Map) MetaUtils.extractValuesFromObject(l_message); - - assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("size"), Matchers.equalTo(-1)); - assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); - //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); - assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); - var contentList = (List>) l_result.get("content"); - assertThat(contentList.size(), Matchers.equalTo(2)); - assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); - assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); - assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); - } - - } diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java new file mode 100644 index 00000000..0e5adb99 --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java @@ -0,0 +1,202 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service; + +import com.adobe.campaign.tests.bridge.plugins.deserializer.MimeExtractionPluginDeserializer; +import com.adobe.campaign.tests.bridge.service.data.plugins.badctor.BadExtractionPlugin_abstract; +import com.adobe.campaign.tests.bridge.service.data.plugins.badctor.BadExtractionPlugin_badConstructor; +import com.adobe.campaign.tests.bridge.service.data.plugins.badctor.BadExtractionPlugin_ctorThrowsException; +import com.adobe.campaign.tests.bridge.service.exceptions.IBSConfigurationException; +import com.adobe.campaign.tests.bridge.testdata.one.MimeMessageMethods; +import org.hamcrest.Matchers; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.mail.Message; +import javax.mail.MessagingException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class PluginTests { + @BeforeMethod + @AfterClass + public void setUp() { + ConfigValueHandlerIBS.resetAllValues(); + IBSPluginManager.ExtractionPlugins.clearPlugins(); + } + + /////// Tests for the MultiPartMime plugin + + @Test + public void testThePluginManager() throws MessagingException { + var l_suffix = "def"; + + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + + //add plugin + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + //remove plugin + IBSPluginManager.ExtractionPlugins.clearPlugins(); + assertThat("The plugin should be absent", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + } + + @Test + public void testThePluginManagerUsageLevel1() throws MessagingException { + var l_suffix = "def"; + + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + + //add plugin + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + assertThat("The plugin should be added", IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + Map l_result = (Map) IBSPluginManager.ExtractionPlugins.apply(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + @Test + public void testUsageOfTheExtractionPlugIn() throws MessagingException { + var l_suffix = "abc"; + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + assertThat("Since no plugin has been declared content should be generated by default", l_result.get("content"), + Matchers.not(Matchers.instanceOf(List.class))); + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + + l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + @Test + public void testUsageOfTheExtractionPlugInConfigBased() throws MessagingException { + + var l_suffix = "abc"; + Message l_message = MimeMessageMethods.createMultiPartAlternativeMessage(l_suffix); + + Map l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + assertThat("Since no plugin has been declared content should be generated by default", l_result.get("content"), + Matchers.not(Matchers.instanceOf(List.class))); + + ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.activate( + "com.adobe.campaign.tests.bridge.plugins.deserializer"); + IBSPluginManager.loadPlugins(); + + l_result = (Map) MetaUtils.extractValuesFromObject(l_message); + + assertThat(l_result.get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("size"), Matchers.equalTo(-1)); + assertThat(l_result.get("subject"), Matchers.equalTo("This is the Subject Line " + l_suffix)); + //assertThat(l_result.get("content"), Matchers.equalTo("a content by yours truly " + l_suffix)); + assertThat(l_result.get("content"), Matchers.instanceOf(List.class)); + var contentList = (List>) l_result.get("content"); + assertThat(contentList.size(), Matchers.equalTo(2)); + assertThat(contentList.get(0), Matchers.instanceOf(Map.class)); + assertThat(contentList.get(0).get("contentType"), Matchers.equalTo("text/plain")); + assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); + } + + //Negative tests + @Test + public void testExceptionsDuringPluginManager_badCtor() { + try { + IBSPluginManager.ExtractionPlugins.addPlugin( + BadExtractionPlugin_badConstructor.class); + assertThat("An exception should have been thrown.", false); + + } catch (Exception e) { + assertThat("We should encapsulate the exception into an IBS Config Exception", e, + Matchers.instanceOf(IBSConfigurationException.class)); + assertThat("The root cause should be that there is no default constructor", e.getCause(), + Matchers.instanceOf(NoSuchMethodException.class)); + } + } + + @Test + public void testExceptionsDuringPluginManager_invocationTarget() throws NoSuchMethodException { + try { + IBSPluginManager.ExtractionPlugins.addPlugin( + BadExtractionPlugin_ctorThrowsException.class); + assertThat("An exception should have been thrown.", false); + + } catch (Exception e) { + assertThat("We should encapsulate the exception into an IBS Config Exception", e, + Matchers.instanceOf(IBSConfigurationException.class)); + assertThat("The root cause should be an Invocation Target Exception", e.getCause(), + Matchers.instanceOf(InvocationTargetException.class)); + } + } + + @Test + public void testExceptionsDuringPluginManager_illegalAccess() throws NoSuchMethodException { + + ConfigValueHandlerIBS.PLUGIN_DESERIALIZATION_PATH.activate( + "com.adobe.campaign.tests.bridge.service.data.plugins.badctor2"); + try { + IBSPluginManager.loadPlugins(); + assertThat("An exception should have been thrown.", false); + + } catch (Exception e) { + assertThat("We should encapsulate the exception into an IBS Config Exception", e, + Matchers.instanceOf(IBSConfigurationException.class)); + assertThat("The root cause should be an Illegal access exception", e.getCause(), + Matchers.instanceOf(IllegalAccessException.class)); + } + } + + @Test + public void testExceptionsDuringPluginManager_instantiationException() throws NoSuchMethodException { + + try { + IBSPluginManager.ExtractionPlugins.addPlugin( + BadExtractionPlugin_abstract.class); + assertThat("An exception should have been thrown.", false); + + } catch (Exception e) { + assertThat("We should encapsulate the exception into an IBS Config Exception", e, + Matchers.instanceOf(IBSConfigurationException.class)); + assertThat("The root cause should be an Invocation Target Exception", e.getCause(), + Matchers.instanceOf(InstantiationException.class)); + } + } + +} diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_abstract.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_abstract.java new file mode 100644 index 00000000..b0d24f8f --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_abstract.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service.data.plugins.badctor; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; + +import java.util.Map; + +public abstract class BadExtractionPlugin_abstract implements IBSDeserializerPlugin { + public BadExtractionPlugin_abstract() { + } + + @Override + public boolean appliesTo(Object in_object) { + return false; + } + + @Override + public Map apply(Object in_object) { + return null; + } +} diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_badConstructor.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_badConstructor.java new file mode 100644 index 00000000..dce0cb95 --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_badConstructor.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service.data.plugins.badctor; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; + +import java.util.Map; + +public class BadExtractionPlugin_badConstructor implements IBSDeserializerPlugin { + + public BadExtractionPlugin_badConstructor(String in_string) { + } + + @Override + public boolean appliesTo(Object in_object) { + return true; + } + + @Override + public Map apply(Object in_object) { + return null; + } +} diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_ctorThrowsException.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_ctorThrowsException.java new file mode 100644 index 00000000..3a9e199f --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor/BadExtractionPlugin_ctorThrowsException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service.data.plugins.badctor; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; + +import java.util.Map; + +public class BadExtractionPlugin_ctorThrowsException implements IBSDeserializerPlugin { + public BadExtractionPlugin_ctorThrowsException() { + throw new RuntimeException(); + } + + @Override + public boolean appliesTo(Object in_object) { + return false; + } + + @Override + public Map apply(Object in_object) { + return null; + } +} diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor2/BadExtractionPlugin_ctorIllegalAccess.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor2/BadExtractionPlugin_ctorIllegalAccess.java new file mode 100644 index 00000000..502c26a2 --- /dev/null +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/data/plugins/badctor2/BadExtractionPlugin_ctorIllegalAccess.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.bridge.service.data.plugins.badctor2; + +import com.adobe.campaign.tests.bridge.plugins.IBSDeserializerPlugin; + +import java.util.Map; + +class BadExtractionPlugin_ctorIllegalAccess implements IBSDeserializerPlugin { + public BadExtractionPlugin_ctorIllegalAccess() { + } + + @Override + public boolean appliesTo(Object in_object) { + return false; + } + + @Override + public Map apply(Object in_object) { + return null; + } +} From 4fab877f97eb64877974180b709da03507318673 Mon Sep 17 00:00:00 2001 From: baubakg Date: Wed, 11 Sep 2024 15:07:39 +0200 Subject: [PATCH 3/6] Updated release notes --- ReleaseNotes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index b2bf8b0c..e5409fa5 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,6 @@ # Bridge Service - RELEASE NOTES -## 2.11.17 +## 2.11.17 In-Progress +* **New Feature** [#160 Introduce Extraction Plugins](https://github.com/adobe/bridgeService/issues/160). We have now introduced a new plugin mechanism so you can define how an object you are expecting should be deserialized. Please refer to the chapter on ["Deserialization Plugins"](README.md#deserialization-plugins) in the README doc. * [#159 Errors when deserializing Milti-Part Mime Object](https://github.com/adobe/bridgeService/issues/159): We have included a couple of resilience features to better handle the deserialization of complex Objects. This includes: * Nested scraping. We allow a nested scraping of objects. * Ignoring calls that throw errors. We now log the error and continue with the next call. From 51a3b4a5a20cf7491c500461697b2f90e5bfe59d Mon Sep 17 00:00:00 2001 From: baubakg Date: Wed, 11 Sep 2024 15:53:02 +0200 Subject: [PATCH 4/6] Fixing sonar issue --- .../tests/bridge/service/IBSPluginManager.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java index f55a131f..889f21cf 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/IBSPluginManager.java @@ -14,9 +14,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class IBSPluginManager { @@ -29,7 +27,12 @@ static class ExtractionPlugins { static Set plugins = new LinkedHashSet<>(); static Map apply(Object in_object) { - return plugins.stream().filter(a -> a.appliesTo(in_object)).findFirst().get().apply(in_object); + Optional applicablePlugin = plugins.stream().filter(a -> a.appliesTo(in_object)).findFirst(); + + if (!applicablePlugin.isPresent()) { + return new HashMap<>(); + } + return applicablePlugin.get().apply(in_object); } static void clearPlugins() { From 794f9664f6bc2f1a750733592ff7a65b68f83316 Mon Sep 17 00:00:00 2001 From: baubakg Date: Wed, 11 Sep 2024 16:02:16 +0200 Subject: [PATCH 5/6] fixed coverage issues --- .../tests/bridge/service/PluginTests.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java index 0e5adb99..edef0a33 100644 --- a/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java +++ b/integroBridgeService/src/test/java/com/adobe/campaign/tests/bridge/service/PluginTests.java @@ -81,6 +81,22 @@ public void testThePluginManagerUsageLevel1() throws MessagingException { assertThat(l_result.get("lineCount"), Matchers.equalTo(-1)); } + @Test + public void testThePluginManagerUsage_negative() throws MessagingException { + + MimeExtractionPluginDeserializer mimePlugin = new MimeExtractionPluginDeserializer(); + + String l_message = "This is a string"; + + //add plugin + IBSPluginManager.ExtractionPlugins.plugins.add(mimePlugin); + assertThat("The MimePlugin Manager should not apply to a String", !IBSPluginManager.ExtractionPlugins.appliesTo(l_message)); + + Map l_result = (Map) IBSPluginManager.ExtractionPlugins.apply(l_message); + + assertThat("The MimePlugin Manager should not apply to a String", l_result, Matchers.anEmptyMap()); + } + @Test public void testUsageOfTheExtractionPlugIn() throws MessagingException { var l_suffix = "abc"; From d2668903be36c5eced580de6a131b05ade616c80 Mon Sep 17 00:00:00 2001 From: baubakg Date: Wed, 11 Sep 2024 16:07:04 +0200 Subject: [PATCH 6/6] Updating description --- .../campaign/tests/bridge/service/ConfigValueHandlerIBS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java index 99041372..9b49e1ce 100644 --- a/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java +++ b/integroBridgeService/src/main/java/com/adobe/campaign/tests/bridge/service/ConfigValueHandlerIBS.java @@ -66,7 +66,7 @@ public void activate(String in_value) { DESERIALIZATION_DEPTH_LIMIT("IBS.DESERIALIZATION.DEPTH.LIMIT", "1", false, "This value sets the maximum depth of the deserialization."), PLUGIN_DESERIALIZATION_PATH( - "IBS.PLUGINS.PATH", null, false, "The package path to look for the plugins."); + "IBS.PLUGINS.PATH", null, false, "The package path in which IBS should search for the plugins."); public final String systemName; public final String defaultValue;