Repository: telstra/open-kilda
Issue: Flow endpoint with server42-enabled switch has rule discrepancies

Create a flow with at least one endpoint being a server42-enabled switch.
Validate the switch
**Expected:** no rule discrepancies, all rules appear in 'proper' section
**Actual:** `EXCLUSION_FLOW` type rule appears in 'missing' section, `SERVER_42_INGRESS` type rule appears in 'excess' section

- rules are not getting synchronized via sync switch api
- also reproduces during switch update or partial update

Changed files:
src-java/kilda-persistence-neo4j/src/main/java/org/openkilda/persistence/SimpleConversionCallback.java
src-java/kilda-persistence-neo4j/src/test/java/org/openkilda/persistence/SimpleConversionCallbackTest.java
src-java/kilda-model/src/main/java/org/openkilda/model/cookie/Cookie.java

diff --git a/src-java/kilda-model/src/main/java/org/openkilda/model/cookie/Cookie.java b/src-java/kilda-model/src/main/java/org/openkilda/model/cookie/Cookie.java
index ddcd2ba7b..3a434feba 100644
--- a/src-java/kilda-model/src/main/java/org/openkilda/model/cookie/Cookie.java
+++ b/src-java/kilda-model/src/main/java/org/openkilda/model/cookie/Cookie.java
@@ -91,7 +91,7 @@ public class Cookie extends CookieBase implements Comparable<Cookie> {
     }
 
     /**
-     * Conver existing {@link Cookie} instance into {@link CookieBuilder}.
+     * Convert existing {@link Cookie} instance into {@link CookieBuilder}.
      */
     public CookieBuilder toBuilder() {
         return new CookieBuilder()
diff --git a/src-java/kilda-persistence-neo4j/src/main/java/org/openkilda/persistence/SimpleConversionCallback.java b/src-java/kilda-persistence-neo4j/src/main/java/org/openkilda/persistence/SimpleConversionCallback.java
index 95d8c2cf3..f5ae80be3 100644
--- a/src-java/kilda-persistence-neo4j/src/main/java/org/openkilda/persistence/SimpleConversionCallback.java
+++ b/src-java/kilda-persistence-neo4j/src/main/java/org/openkilda/persistence/SimpleConversionCallback.java
@@ -88,7 +88,31 @@ class SimpleConversionCallback implements ConversionCallback {
             throw new PersistenceException("Unable to locate appropriate converter for null-valued target type and "
                     + value);
         }
-        // Look up for a converter with corresponding entity and graph classes.
+        // Look up for a converter with exact entity and graph classes.
+        for (Map.Entry<ParameterizedType, Class<? extends AttributeConverter>> converter : converters.entrySet()) {
+            Type[] genericTypes = converter.getKey().getActualTypeArguments();
+            Class entityClass = (Class) genericTypes[0];
+            Class graphClass = (Class) genericTypes[1];
+
+            if (targetType.equals(entityClass) && graphClass.equals(value.getClass())) {
+                try {
+                    return (T) converter.getValue().newInstance().toEntityAttribute(value);
+                } catch (InstantiationException | IllegalAccessException ex) {
+                    throw new PersistenceException("Unable to instantiate the converter "
+                            + converter.getValue().getSimpleName(), ex);
+                }
+            }
+
+            if (targetType.equals(graphClass) && entityClass.equals(value.getClass())) {
+                try {
+                    return (T) converter.getValue().newInstance().toGraphProperty(value);
+                } catch (InstantiationException | IllegalAccessException ex) {
+                    throw new PersistenceException("Unable to instantiate the converter "
+                            + converter.getValue().getSimpleName(), ex);
+                }
+            }
+        }
+        // Look up for a converter with assignable entity and graph classes.
         for (Map.Entry<ParameterizedType, Class<? extends AttributeConverter>> converter : converters.entrySet()) {
             Type[] genericTypes = converter.getKey().getActualTypeArguments();
             Class entityClass = (Class) genericTypes[0];
@@ -98,7 +122,7 @@ class SimpleConversionCallback implements ConversionCallback {
                 try {
                     return (T) converter.getValue().newInstance().toEntityAttribute(value);
                 } catch (InstantiationException | IllegalAccessException ex) {
-                    throw new PersistenceException("Unable to instaniate the converter "
+                    throw new PersistenceException("Unable to instantiate the converter "
                             + converter.getValue().getSimpleName(), ex);
                 }
             }
@@ -107,7 +131,7 @@ class SimpleConversionCallback implements ConversionCallback {
                 try {
                     return (T) converter.getValue().newInstance().toGraphProperty(value);
                 } catch (InstantiationException | IllegalAccessException ex) {
-                    throw new PersistenceException("Unable to instaniate the converter "
+                    throw new PersistenceException("Unable to instantiate the converter "
                             + converter.getValue().getSimpleName(), ex);
                 }
             }
diff --git a/src-java/kilda-persistence-neo4j/src/test/java/org/openkilda/persistence/SimpleConversionCallbackTest.java b/src-java/kilda-persistence-neo4j/src/test/java/org/openkilda/persistence/SimpleConversionCallbackTest.java
index 80ced2aa2..8be2fda21 100644
--- a/src-java/kilda-persistence-neo4j/src/test/java/org/openkilda/persistence/SimpleConversionCallbackTest.java
+++ b/src-java/kilda-persistence-neo4j/src/test/java/org/openkilda/persistence/SimpleConversionCallbackTest.java
@@ -18,6 +18,9 @@ package org.openkilda.persistence;
 import static org.junit.Assert.assertEquals;
 
 import org.openkilda.model.SwitchId;
+import org.openkilda.model.cookie.FlowSegmentCookie;
+import org.openkilda.persistence.converters.CookieConverter;
+import org.openkilda.persistence.converters.ExclusionCookieConverter;
 import org.openkilda.persistence.converters.SwitchIdConverter;
 
 import org.junit.Test;
@@ -55,5 +58,20 @@ public class SimpleConversionCallbackTest {
         // then
         assertEquals(entity, actualEntity);
     }
+
+    @Test
+    public void shouldUseExactTypeConverterWhenMultipleAvailable() {
+        // given
+        SimpleConversionCallback conversionCallback =
+                new SimpleConversionCallback(Arrays.asList(CookieConverter.class,
+                        ExclusionCookieConverter.class));
+        long graphObject = 1L;
+
+        //when
+        FlowSegmentCookie cookie = conversionCallback.convert(FlowSegmentCookie.class, graphObject);
+
+        // then
+        assertEquals(cookie.getClass(), FlowSegmentCookie.class);
+    }
 }