Merge "Bug 6679 - api explorer creates false examples"
authorTomas Cere <tcere@cisco.com>
Thu, 13 Oct 2016 08:38:28 +0000 (08:38 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 13 Oct 2016 08:38:28 +0000 (08:38 +0000)
19 files changed:
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/TesttoolParameters.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/RestConnectorProvider.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/AbstractIdentifierAwareJaxRsProvider.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/impl/RestconfModulesServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/impl/RestconfOperationsServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/Draft17AbstractBodyReaderTest.java with 78% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/Draft17JsonBodyReaderTest.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestDraft17JsonPATCHBodyReader.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/Draft17XmlBodyReaderTest.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestDraft17XmlPATCHBodyReader.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfModulesServiceTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/parser/ParserIdentifierTest.java

index 79e849a32428e5046f1d6173a717cf5bc1bffa90..8218d73a15acc669599b0d03790b978ee9060076 100644 (file)
@@ -19,11 +19,13 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.StringJoiner;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -278,7 +280,7 @@ public class TesttoolParameters {
 
         if (controllerDestination != null) {
             Preconditions.checkArgument(controllerDestination.contains(":"), "Controller Destination needs to be in a following format <ip>:<port>");
-            String[] parts = controllerDestination.split(Pattern.quote(":"));
+            final String[] parts = controllerDestination.split(Pattern.quote(":"));
             Preconditions.checkArgument(Integer.parseInt(parts[1]) > 0, "Port =< 0");
         }
 
@@ -295,14 +297,14 @@ public class TesttoolParameters {
             for (final File file : files) {
                 final Matcher matcher = YANG_FILENAME_PATTERN.matcher(file.getName());
                 if (!matcher.matches()) {
-                    BufferedReader reader;
+                    final BufferedReader reader;
                     try {
                         reader = new BufferedReader(new FileReader(file));
                         String line = reader.readLine();
                         while (!DATE_PATTERN.matcher(line).find()) {
                             line = reader.readLine();
                         }
-                        Matcher m = DATE_PATTERN.matcher(line);
+                        final Matcher m = DATE_PATTERN.matcher(line);
 
                         if (m.find()) {
                             String moduleName = file.getAbsolutePath();
@@ -310,12 +312,12 @@ public class TesttoolParameters {
                                 moduleName = moduleName.substring(0, moduleName.length() - 5);
                             }
                             final String revision = m.group(1);
-                            String correctName = moduleName + "@" + revision + ".yang";
-                            File correctNameFile = new File(correctName);
+                            final String correctName = moduleName + "@" + revision + ".yang";
+                            final File correctNameFile = new File(correctName);
                             file.renameTo(correctNameFile);
                         }
 
-                    } catch (IOException e) {
+                    } catch (final IOException e) {
                         e.printStackTrace();
                     }
                 }
@@ -400,7 +402,7 @@ public class TesttoolParameters {
     }
 
     private String prepareMessage(final int openDevice, final String editContentString) {
-        StringBuilder messageBuilder = new StringBuilder(editContentString);
+        final StringBuilder messageBuilder = new StringBuilder(editContentString);
 
         if (editContentString.contains(HOST_KEY)) {
             messageBuilder.replace(messageBuilder.indexOf(HOST_KEY), messageBuilder.indexOf(HOST_KEY) + HOST_KEY.length(), generateConfigsAddress);
@@ -441,33 +443,22 @@ public class TesttoolParameters {
         return payloads;
     }
 
-    //TODO This may be more scalable enumerating parameters via reflection
     @Override
     public String toString() {
-        StringBuffer params = new StringBuffer("TesttoolParameters{");
-        params.append("edit-content='").append(editContent).append('\'');
-        params.append(", async='").append(async).append('\'');
-        params.append(", thread-amount='").append(threadAmount).append('\'');
-        params.append(", throttle='").append(throttle).append('\'');
-        params.append(", auth='").append(auth).append('\'');
-        params.append(", controller-destination='").append(controllerDestination).append('\'');
-        params.append(", schemas-dir='").append(schemasDir).append('\'');
-        params.append(", devices-count='").append(deviceCount).append('\'');
-        params.append(", devices-per-port='").append(devicesPerPort).append('\'');
-        params.append(", starting-port='").append(startingPort).append('\'');
-        params.append(", generate-config-connection-timeout='").append(generateConfigsTimeout).append('\'');
-        params.append(", generate-config-address='").append(generateConfigsAddress).append('\'');
-        params.append(", distro-folder='").append(distroFolder).append('\'');
-        params.append(", generate-configs-batch-size='").append(generateConfigBatchSize).append('\'');
-        params.append(", ssh='").append(ssh).append('\'');
-        params.append(", exi='").append(exi).append('\'');
-        params.append(", debug='").append(debug).append('\'');
-        params.append(", notification-file='").append(notificationFile).append('\'');
-        params.append(", md-sal='").append(mdSal).append('\'');
-        params.append(", initial-config-xml-file='").append(initialConfigXMLFile).append('\'');
-        params.append(", time-out='").append(timeOut).append('\'');
-        params.append('}');
-
-        return params.toString();
+        final List<Field> fields = Arrays.asList(this.getClass().getDeclaredFields());
+        final StringJoiner joiner = new StringJoiner(", \n", "TesttoolParameters{", "}\n");
+        fields.stream()
+                .filter(field -> field.getAnnotation(Arg.class) != null)
+                .map(this::getFieldString)
+                .forEach(joiner::add);
+        return joiner.toString();
+    }
+
+    private String getFieldString(final Field field) {
+        try {
+            return field.getName() + "='" + field.get(this) + "'";
+        } catch (final IllegalAccessException e) {
+            return field.getName() + "= UNKNOWN";
+        }
     }
 }
index 50012e643b4df92fca954fa7c407fc43f384be58..846320049d4f2bbb46e47698f8cd6967e395d7f3 100644 (file)
@@ -59,6 +59,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
     private ListenerRegistration<SchemaContextListener> listenerRegistration;
     private static TransactionChainHandler transactionChainHandler;
     private static DOMDataBroker dataBroker;
+    private static DOMMountPointServiceHandler mountPointServiceHandler;
 
     @Override
     public void onSessionInitiated(final ProviderSession session) {
@@ -69,7 +70,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         final SchemaContextHandler schemaCtxHandler = new SchemaContextHandler();
         this.listenerRegistration = schemaService.registerSchemaContextListener(schemaCtxHandler);
 
-        final DOMMountPointServiceHandler domMountPointServiceHandler = new DOMMountPointServiceHandler(
+        RestConnectorProvider.mountPointServiceHandler = new DOMMountPointServiceHandler(
                 session.getService(DOMMountPointService.class));
 
         RestConnectorProvider.dataBroker = session.getService(DOMDataBroker.class);
@@ -81,7 +82,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         final DOMRpcService rpcService = session.getService(DOMRpcService.class);
         final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
 
-        wrapperServices.setHandlers(schemaCtxHandler, domMountPointServiceHandler,
+        wrapperServices.setHandlers(schemaCtxHandler, RestConnectorProvider.mountPointServiceHandler,
                 RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler);
     }
 
@@ -100,6 +101,14 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         );
     }
 
+    /**
+     * Get current {@link DOMMountPointService} from {@link DOMMountPointServiceHandler}.
+     * @return {@link DOMMountPointService}
+     */
+    public static DOMMountPointService getMountPointService() {
+        return RestConnectorProvider.mountPointServiceHandler.get();
+    }
+
     @Override
     public Collection<ProviderFunctionality> getProviderFunctionality() {
         return Collections.emptySet();
index 0e7999b2563664506b24f69ab3163737d3f829e1..ce948af3ea1e92101cf6cd19350221fde8a6ebfd 100644 (file)
@@ -138,7 +138,7 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
 
     @Override
     public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
-            final UriInfo uriInfo) {
+                                           final UriInfo uriInfo) {
         return this.delegRestconfInvokeOpsService.invokeRpc(identifier, payload, uriInfo);
     }
 
@@ -148,14 +148,16 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
     }
 
     public void setHandlers(final SchemaContextHandler schemaCtxHandler,
-            final DOMMountPointServiceHandler domMountPointServiceHandler,
-            final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler,
-            final RpcServiceHandler rpcServiceHandler) {
+                            final DOMMountPointServiceHandler domMountPointServiceHandler,
+                            final TransactionChainHandler transactionChainHandler,
+                            final DOMDataBrokerHandler domDataBrokerHandler,
+                            final RpcServiceHandler rpcServiceHandler) {
         this.delegRestModService = new RestconfModulesServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestStrsService = new RestconfStreamsServiceImpl(schemaCtxHandler);
-        this.delegRestconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler);
+        this.delegRestconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler,
+                domMountPointServiceHandler);
         this.delegRestconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler,
                 schemaCtxHandler);
         this.delegRestconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler);
index bcf95f7b6450ccb9925316951f773d8fc0b78aa4..69ebec275128f95f4eaaec1cf7fb6f0cbc96014b 100644 (file)
@@ -8,12 +8,14 @@
 
 package org.opendaylight.restconf.jersey.providers;
 
+import com.google.common.base.Optional;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 
 public class AbstractIdentifierAwareJaxRsProvider {
@@ -31,8 +33,10 @@ public class AbstractIdentifierAwareJaxRsProvider {
     }
 
     protected InstanceIdentifierContext<?> getInstanceIdentifierContext() {
-        return ParserIdentifier.toInstanceIdentifier(getIdentifier(),
-                ControllerContext.getInstance().getGlobalSchema());
+        return ParserIdentifier.toInstanceIdentifier(
+                getIdentifier(),
+                ControllerContext.getInstance().getGlobalSchema(),
+                Optional.of(RestConnectorProvider.getMountPointService()));
     }
 
     protected UriInfo getUriInfo() {
index e4bc1b423988e386efef7729538a5b3451d434eb..5b7a6ebd999441b36f456288e8e4632d2b86552b 100644 (file)
@@ -7,12 +7,12 @@
  */
 package org.opendaylight.restconf.rest.services.impl;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.util.Collections;
 import java.util.Set;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -80,11 +80,9 @@ public class RestconfModulesServiceImpl implements RestconfModulesService {
             throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
         }
         final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
-        final InstanceIdentifierContext<?> mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-                schemaContextRef.get());
-        final DOMMountPointService domMointPointService = this.domMountPointServiceHandler.get();
-        final DOMMountPoint mountPoint = domMointPointService
-                .getMountPoint(mountPointIdentifier.getInstanceIdentifier()).get();
+        final InstanceIdentifierContext<?> mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(
+                identifier, schemaContextRef.get(), Optional.of(this.domMountPointServiceHandler.get()));
+        final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
         return getModules(mountPoint.getSchemaContext().getModules(), schemaContextRef, mountPoint);
     }
 
@@ -94,13 +92,15 @@ public class RestconfModulesServiceImpl implements RestconfModulesService {
         Preconditions.checkNotNull(identifier);
         final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
         final QName moduleQname = ParserIdentifier.makeQNameFromIdentifier(identifier);
-        Module module = null;
+        final Module module;
         DOMMountPoint mountPoint = null;
         if (identifier.contains(RestconfConstants.MOUNT)) {
-            final InstanceIdentifierContext<?> point = ParserIdentifier.toInstanceIdentifier(identifier,
-                    schemaContextRef.get());
-            final DOMMountPointService domMointPointService = this.domMountPointServiceHandler.get();
-            mountPoint = domMointPointService.getMountPoint(point.getInstanceIdentifier()).get();
+            // we only need to find mount point itself
+            final String mountPointPath = identifier.substring(
+                    0, identifier.indexOf(RestconfConstants.MOUNT) + RestconfConstants.MOUNT.length());
+            final InstanceIdentifierContext<?> mountPointContext = ParserIdentifier.toInstanceIdentifier(
+                    mountPointPath, schemaContextRef.get(), Optional.of(this.domMountPointServiceHandler.get()));
+            mountPoint = mountPointContext.getMountPoint();
             module = schemaContextRef.findModuleInMountPointByQName(mountPoint, moduleQname);
         } else {
             module = schemaContextRef.findModuleByQName(moduleQname);
index bddd086bd702a5427d63c99000a66df654ad74ea..e14fa8fe6bf8658199ca3750e31f21c04193aa2b 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.rest.services.impl;
 
+import com.google.common.base.Optional;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -73,12 +74,12 @@ public class RestconfOperationsServiceImpl implements RestconfOperationsService
 
     @Override
     public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
-        Set<Module> modules = null;
-        DOMMountPoint mountPoint = null;
+        final Set<Module> modules;
+        final DOMMountPoint mountPoint;
         final SchemaContextRef ref = new SchemaContextRef(this.schemaContextHandler.get());
         if (identifier.contains(RestconfConstants.MOUNT)) {
-            final InstanceIdentifierContext<?> mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-                    ref.get());
+            final InstanceIdentifierContext<?> mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(
+                    identifier, ref.get(), Optional.of(this.domMountPointServiceHandler.get()));
             mountPoint = mountPointIdentifier.getMountPoint();
             modules = ref.getModules(mountPoint);
 
index c27da7ab621e240beac41202bccc7c5bab5bd86e..cb17434392c1870ce602082d4719e8366a671abf 100644 (file)
@@ -12,10 +12,10 @@ import com.google.common.base.Preconditions;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.TimeZone;
+import javax.annotation.Nonnull;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
@@ -26,6 +26,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
@@ -51,11 +52,14 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
     private final SchemaContextHandler schemaContextHandler;
     private final TransactionChainHandler transactionChainHandler;
+    private final DOMMountPointServiceHandler mountPointServiceHandler;
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
-                                   final TransactionChainHandler transactionChainHandler) {
+                                   final TransactionChainHandler transactionChainHandler,
+                                   final DOMMountPointServiceHandler mountPointServiceHandler) {
         this.schemaContextHandler = schemaContextHandler;
         this.transactionChainHandler = transactionChainHandler;
+        this.mountPointServiceHandler = mountPointServiceHandler;
     }
 
     @Override
@@ -63,20 +67,20 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         Preconditions.checkNotNull(identifier);
         final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
 
-        final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-                schemaContextRef.get());
+        final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
+                identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final String value = uriInfo.getQueryParameters().getFirst(RestconfDataServiceConstant.CONTENT);
 
-        final DOMTransactionChain transaction;
+        final DOMTransactionChain transactionChain;
         if (mountPoint == null) {
-            transaction = this.transactionChainHandler.get();
+            transactionChain = this.transactionChainHandler.get();
         } else {
-            transaction = transactionOfMountPoint(mountPoint);
+            transactionChain = transactionChainOfMountPoint(mountPoint);
         }
 
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
-                transaction);
+                transactionChain);
         final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(value, transactionNode);
         if (node == null) {
             throw new RestconfDocumentedException(
@@ -111,18 +115,18 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        final DOMTransactionChain transaction;
+        final DOMTransactionChain transactionChain;
         final SchemaContextRef ref;
         if (mountPoint == null) {
-            transaction = this.transactionChainHandler.get();
+            transactionChain = this.transactionChainHandler.get();
             ref = new SchemaContextRef(this.schemaContextHandler.get());
         } else {
-            transaction = transactionOfMountPoint(mountPoint);
+            transactionChain = transactionChainOfMountPoint(mountPoint);
             ref = new SchemaContextRef(mountPoint.getSchemaContext());
         }
 
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                payload.getInstanceIdentifierContext(), mountPoint, transaction);
+                payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
         return PutDataTransactionUtil.putData(payload, ref, transactionNode);
     }
 
@@ -136,36 +140,36 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         Preconditions.checkNotNull(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        final DOMTransactionChain transaction;
+        final DOMTransactionChain transactionChain;
         final SchemaContextRef ref;
         if (mountPoint == null) {
-            transaction = this.transactionChainHandler.get();
+            transactionChain = this.transactionChainHandler.get();
             ref = new SchemaContextRef(this.schemaContextHandler.get());
         } else {
-            transaction = transactionOfMountPoint(mountPoint);
+            transactionChain = transactionChainOfMountPoint(mountPoint);
             ref = new SchemaContextRef(mountPoint.getSchemaContext());
         }
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                payload.getInstanceIdentifierContext(), mountPoint, transaction);
+                payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
         return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref);
     }
 
     @Override
     public Response deleteData(final String identifier) {
         final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
-        final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-                schemaContextRef.get());
+        final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
+                identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
-        final DOMTransactionChain transaction;
+        final DOMTransactionChain transactionChain;
         if (mountPoint == null) {
-            transaction = this.transactionChainHandler.get();
+            transactionChain = this.transactionChainHandler.get();
         } else {
-            transaction = transactionOfMountPoint(mountPoint);
+            transactionChain = transactionChainOfMountPoint(mountPoint);
         }
 
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
-                transaction);
+                transactionChain);
         return DeleteDataTransactionUtil.deleteData(transactionNode);
     }
 
@@ -179,29 +183,29 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         Preconditions.checkNotNull(context);
         final DOMMountPoint mountPoint = context.getInstanceIdentifierContext().getMountPoint();
 
-        final DOMTransactionChain transaction;
+        final DOMTransactionChain transactionChain;
         final SchemaContextRef ref;
         if (mountPoint == null) {
-            transaction = this.transactionChainHandler.get();
+            transactionChain = this.transactionChainHandler.get();
             ref = new SchemaContextRef(this.schemaContextHandler.get());
         } else {
-            transaction = transactionOfMountPoint(mountPoint);
+            transactionChain = transactionChainOfMountPoint(mountPoint);
             ref = new SchemaContextRef(mountPoint.getSchemaContext());
         }
 
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                context.getInstanceIdentifierContext(), mountPoint, transaction);
+                context.getInstanceIdentifierContext(), mountPoint, transactionChain);
 
         return PatchDataTransactionUtil.patchData(context, transactionNode, ref);
     }
 
     /**
-     * Prepare transaction to read data of mount point, if these data are
-     * present.
+     * Prepare transaction chain to access data of mount point
      * @param mountPoint
-     * @return {@link DOMDataReadWriteTransaction}
+     *            - mount point reference
+     * @return {@link DOMTransactionChain}
      */
-    private static DOMTransactionChain transactionOfMountPoint(final DOMMountPoint mountPoint) {
+    private static DOMTransactionChain transactionChainOfMountPoint(@Nonnull final DOMMountPoint mountPoint) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
             return domDataBrokerService.get().createTransactionChain(RestConnectorProvider.transactionListener);
index 6dc8a9463622fcf36421aef22420e455bc186dd5..fe840990ed1f4db44d5eb076c9e3dc9aea81170b 100644 (file)
@@ -8,10 +8,13 @@
 package org.opendaylight.restconf.restful.utils;
 
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import java.util.Collection;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -19,23 +22,23 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 
 /**
@@ -62,7 +65,8 @@ public final class ReadDataTransactionUtil {
      *            - {@link TransactionVarsWrapper} - wrapper for variables
      * @return {@link NormalizedNode}
      */
-    public static NormalizedNode<?, ?> readData(final String valueOfContent, final TransactionVarsWrapper transactionNode) {
+    public static @Nullable NormalizedNode<?, ?> readData(@Nullable final String valueOfContent,
+                                                          @Nonnull final TransactionVarsWrapper transactionNode) {
         final NormalizedNode<?, ?> data;
         if (valueOfContent != null) {
             switch (valueOfContent) {
@@ -78,7 +82,7 @@ public final class ReadDataTransactionUtil {
                     data = readAllData(transactionNode);
                     break;
                 default:
-                    throw new RestconfDocumentedException("Bad querry parameter for content.", ErrorType.APPLICATION,
+                    throw new RestconfDocumentedException("Bad query parameter for content.", ErrorType.APPLICATION,
                             ErrorTag.INVALID_VALUE);
             }
         } else {
@@ -97,14 +101,15 @@ public final class ReadDataTransactionUtil {
      *            - {@link TransactionVarsWrapper} - wrapper for variables
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> readDataViaTransaction(final TransactionVarsWrapper transactionNode) {
-            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = transactionNode
-                    .getTransactionChain().newReadOnlyTransaction().read(transactionNode.getLogicalDatastoreType(),
-                            transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-            final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
-            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                    dataFactory);
-            return dataFactory.build();
+    private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
+            @Nonnull final TransactionVarsWrapper transactionNode) {
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = transactionNode
+                .getTransactionChain().newReadOnlyTransaction().read(transactionNode.getLogicalDatastoreType(),
+                        transactionNode.getInstanceIdentifier().getInstanceIdentifier());
+        final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
+        FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                dataFactory);
+        return dataFactory.build();
     }
 
     /**
@@ -114,7 +119,7 @@ public final class ReadDataTransactionUtil {
      *            - {@link TransactionVarsWrapper} - wrapper for variables
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> readAllData(final TransactionVarsWrapper transactionNode) {
+    private static @Nullable NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode) {
         // PREPARE STATE DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
         final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
@@ -151,8 +156,8 @@ public final class ReadDataTransactionUtil {
      *            - data node of config data
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> mapNode(final NormalizedNode<?, ?> stateDataNode,
-            final NormalizedNode<?, ?> configDataNode) {
+    private static @Nonnull NormalizedNode<?, ?> mapNode(@Nonnull final NormalizedNode<?, ?> stateDataNode,
+                                                         @Nonnull final NormalizedNode<?, ?> configDataNode) {
         validPossibilityOfMergeNodes(stateDataNode, configDataNode);
         if (configDataNode instanceof RpcDefinition) {
             return prepareRpcData(configDataNode, stateDataNode);
@@ -161,6 +166,23 @@ public final class ReadDataTransactionUtil {
         }
     }
 
+    /**
+     * Valid of can be data merged together.
+     *
+     * @param stateDataNode
+     *            - data node of state data
+     * @param configDataNode
+     *            - data node of config data
+     */
+    private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode<?, ?> stateDataNode,
+                                                     @Nonnull final NormalizedNode<?, ?> configDataNode) {
+        final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
+        final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
+        if (moduleOfStateData != moduleOfConfigData) {
+            throw new RestconfDocumentedException("It is not possible to merge ");
+        }
+    }
+
     /**
      * Prepare and map data for rpc
      *
@@ -170,8 +192,8 @@ public final class ReadDataTransactionUtil {
      *            - data node of state data
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> prepareRpcData(final NormalizedNode<?, ?> configDataNode,
-            final NormalizedNode<?, ?> stateDataNode) {
+    private static @Nonnull NormalizedNode<?, ?> prepareRpcData(@Nonnull final NormalizedNode<?, ?> configDataNode,
+                                                                @Nonnull final NormalizedNode<?, ?> stateDataNode) {
         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
                 .mapEntryBuilder();
         mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
@@ -192,11 +214,10 @@ public final class ReadDataTransactionUtil {
      * @param mapEntryBuilder
      *            - builder for mapping data
      */
-    private static void mapRpcDataNode(final NormalizedNode<?, ?> dataNode,
-            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
-        for (final DataContainerChild<? extends PathArgument, ?> child : ((ContainerNode) dataNode).getValue()) {
-            mapEntryBuilder.addChild(child);
-        }
+    private static void mapRpcDataNode(@Nonnull final NormalizedNode<?, ?> dataNode,
+                                       @Nonnull final DataContainerNodeBuilder<
+                                               NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
+        ((ContainerNode) dataNode).getValue().forEach(mapEntryBuilder::addChild);
     }
 
     /**
@@ -208,116 +229,120 @@ public final class ReadDataTransactionUtil {
      *            - data node of state data
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> prepareData(final NormalizedNode<?, ?> configDataNode,
-            final NormalizedNode<?, ?> stateDataNode) {
+    private static @Nonnull NormalizedNode<?, ?> prepareData(@Nonnull final NormalizedNode<?, ?> configDataNode,
+                                                             @Nonnull final NormalizedNode<?, ?> stateDataNode) {
+        if (configDataNode instanceof MapNode) {
+            final CollectionNodeBuilder<MapEntryNode, MapNode> builder = ImmutableNodes
+                    .mapNodeBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
 
-        if (configDataNode instanceof MapNode) { // part for lists mapping
-            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
-                .mapEntryBuilder();
-            final NodeIdentifierWithPredicates node = ((MapNode) configDataNode).getValue().iterator().next().getIdentifier();
-            mapEntryBuilder.withNodeIdentifier(node);
-
-            // MAP CONFIG DATA
-            mapDataNode((MapNode) configDataNode, mapEntryBuilder);
-            // MAP STATE DATA
-            mapDataNode((MapNode) stateDataNode, mapEntryBuilder);
-            return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
-        } else if (configDataNode instanceof ContainerNode) { // part for containers mapping
-            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
-                    .containerBuilder((ContainerNode) configDataNode);
-
-            // MAP CONFIG DATA
-            mapCont(containerBuilder, ((ContainerNode) configDataNode).getValue());
-            // MAP STATE DATA
-            mapCont(containerBuilder, ((ContainerNode) stateDataNode).getValue());
-            return containerBuilder.build();
+            mapValueToBuilder(
+                    ((MapNode) configDataNode).getValue(), ((MapNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof MapEntryNode) {
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = ImmutableNodes
+                    .mapEntryBuilder().withNodeIdentifier(((MapEntryNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((MapEntryNode) configDataNode).getValue(), ((MapEntryNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof ContainerNode) {
+            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders
+                    .containerBuilder().withNodeIdentifier(((ContainerNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((ContainerNode) configDataNode).getValue(), ((ContainerNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof AugmentationNode) {
+            final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder = Builders
+                    .augmentationBuilder().withNodeIdentifier(((AugmentationNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((AugmentationNode) configDataNode).getValue(), ((AugmentationNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof ChoiceNode) {
+            final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders
+                    .choiceBuilder().withNodeIdentifier(((ChoiceNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((ChoiceNode) configDataNode).getValue(), ((ChoiceNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof LeafNode) {
+            return ImmutableNodes.leafNode(configDataNode.getNodeType(), configDataNode.getValue());
         } else {
             throw new RestconfDocumentedException("Bad type of node.");
         }
     }
 
     /**
-     * Map data to builder
+     * Map value from container node to builder.
      *
-     * @param containerBuilder
-     *            - builder for mapping data
-     * @param childs
-     *            - childs of data (container)
+     * @param configData
+     *            - collection of config data nodes
+     * @param stateData
+     *            - collection of state data nodes
+     * @param builder
+     *            - builder
      */
-    private static void mapCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder,
-            final Collection<DataContainerChild<? extends PathArgument, ?>> childs) {
-        for (final DataContainerChild<? extends PathArgument, ?> child : childs) {
-            containerBuilder.addChild(child);
-        }
-    }
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapValueToBuilder(
+            @Nonnull final Collection<T> configData,
+            @Nonnull final Collection<T> stateData,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        final Map<PathArgument, T> configMap = configData.stream().collect(
+                Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
+        final Map<PathArgument, T> stateMap = stateData.stream().collect(
+                Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
 
-    /**
-     * Map data to builder
-     *
-     * @param immutableData
-     *            - immutable data - {@link MapNode}
-     * @param mapEntryBuilder
-     *            - builder for mapping data
-     */
-    private static void mapDataNode(final MapNode immutableData,
-            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
-        for (final DataContainerChild<? extends PathArgument, ?> child : immutableData.getValue().iterator()
-                .next().getValue()) {
-            Preconditions.checkNotNull(child);
-            if (child instanceof ContainerNode) {
-                addChildToMap(ContainerNode.class, child, mapEntryBuilder);
-            } else if (child instanceof AugmentationNode) {
-                addChildToMap(AugmentationNode.class, child, mapEntryBuilder);
-            } else if(child instanceof MapNode){
-                final MapNode listNode = (MapNode) child;
-                for (final MapEntryNode listChild : listNode.getValue()) {
-                    for (final DataContainerChild<? extends PathArgument, ?> entryChild : listChild.getValue()) {
-                        addChildToMap(MapEntryNode.class, entryChild, mapEntryBuilder);
-                    }
-                }
-            } else if (child instanceof ChoiceNode) {
-                addChildToMap(ChoiceNode.class, child, mapEntryBuilder);
-            } else if ((child instanceof LeafSetNode<?>) || (child instanceof LeafNode)) {
-                mapEntryBuilder.addChild(child);
-            }
+        // merge config and state data of children with different identifiers
+        mapDataToBuilder(configMap, stateMap, builder);
 
-        }
+        // merge config and state data of children with the same identifiers
+        mergeDataToBuilder(configMap, stateMap, builder);
     }
 
     /**
-     * Mapping child
+     * Map data with different identifiers to builder. Data with different identifiers can be just added
+     * as childs to parent node.
      *
-     * @param type
-     *            - type of data
-     * @param child
-     *            - child to map
-     * @param mapEntryBuilder
-     *            - builder for mapping child
+     * @param configMap
+     *            - map of config data nodes
+     * @param stateMap
+     *            - map of state data nodes
+     * @param builder
+     *           - builder
      */
-    private static <T extends DataContainerNode<? extends PathArgument>> void addChildToMap(final Class<T> type,
-            final DataContainerChild<? extends PathArgument, ?> child,
-            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
-        @SuppressWarnings("unchecked")
-        final T node = (T) child;
-        for (final DataContainerChild<? extends PathArgument, ?> childNode : node.getValue()) {
-            mapEntryBuilder.addChild(childNode);
-        }
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapDataToBuilder(
+            @Nonnull final Map<PathArgument, T> configMap,
+            @Nonnull final Map<PathArgument, T> stateMap,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        configMap.entrySet().stream().filter(x -> !stateMap.containsKey(x.getKey())).forEach(
+                y -> builder.addChild(y.getValue()));
+        stateMap.entrySet().stream().filter(x -> !configMap.containsKey(x.getKey())).forEach(
+                y -> builder.addChild(y.getValue()));
     }
 
     /**
-     * Valid of can be data merged together.
+     * Map data with the same identifiers to builder. Data with the same identifiers cannot be just added but we need to
+     * go one level down with {@code prepareData} method.
      *
-     * @param stateDataNode
-     *            - data node of state data
-     * @param configDataNode
-     *            - data node of config data
+     * @param configMap
+     *            - immutable config data
+     * @param stateMap
+     *            - immutable state data
+     * @param builder
+     *           - builder
      */
-    private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode<?, ?> stateDataNode,
-            @Nonnull final NormalizedNode<?, ?> configDataNode) {
-        final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
-        final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
-        if (moduleOfStateData != moduleOfConfigData) {
-            throw new RestconfDocumentedException("It is not possible to merge ");
-        }
+    @SuppressWarnings("unchecked")
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mergeDataToBuilder(
+            @Nonnull final Map<PathArgument, T> configMap,
+            @Nonnull final Map<PathArgument, T> stateMap,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        // it is enough to process only config data because operational contains the same data
+        configMap.entrySet().stream().filter(x -> stateMap.containsKey(x.getKey())).forEach(
+                y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));
     }
 }
index d24c3deb07336c99016c453b3810d1cf44f556d9..fc2703f98be3a9eb3a2a4496e2554f0a9e05919d 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.utils.parser;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -14,7 +15,6 @@ import java.text.ParseException;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
-import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
@@ -48,25 +48,62 @@ public final class ParserIdentifier {
     }
 
     /**
-     * Make {@link InstanceIdentifierContext} from identifier.
+     * Make {@link InstanceIdentifierContext} from {@link String} identifier
+     * <br>
+     * For identifiers of data NOT behind mount points returned
+     * {@link InstanceIdentifierContext} is prepared with {@code null} reference of {@link DOMMountPoint} and with
+     * controller's {@link SchemaContext}.
+     * <br>
+     * For identifiers of data behind mount points returned
+     * {@link InstanceIdentifierContext} is prepared with reference of {@link DOMMountPoint} and its
+     * own {@link SchemaContext}.
      *
      * @param identifier
-     *            - path identifier
+     *           - path identifier
      * @param schemaContext
-     *            - {@link SchemaContext}
+     *           - controller schema context
+     * @param mountPointService
+     *           - mount point service
      * @return {@link InstanceIdentifierContext}
      */
-    public static InstanceIdentifierContext<?> toInstanceIdentifier(@Nullable final String identifier,
-            final SchemaContext schemaContext) {
-        final YangInstanceIdentifier deserialize;
+    public static InstanceIdentifierContext<?> toInstanceIdentifier(
+            final String identifier,
+            final SchemaContext schemaContext,
+            final Optional<DOMMountPointService> mountPointService) {
         if (identifier != null && identifier.contains(RestconfConstants.MOUNT)) {
-            final String mountPointId = identifier.substring(0, identifier.indexOf("/" + RestconfConstants.MOUNT));
-            deserialize = IdentifierCodec.deserialize(mountPointId, schemaContext);
+            if (!mountPointService.isPresent()) {
+                throw new RestconfDocumentedException("Mount point service is not available");
+            }
+
+            final Iterator<String> pathsIt = Splitter.on("/" + RestconfConstants.MOUNT).split(identifier).iterator();
+
+            final String mountPointId = pathsIt.next();
+            final YangInstanceIdentifier mountYangInstanceIdentifier = IdentifierCodec.deserialize(
+                    mountPointId, schemaContext);
+            final Optional<DOMMountPoint> mountPoint = mountPointService.get().getMountPoint(mountYangInstanceIdentifier);
+
+            if (!mountPoint.isPresent()) {
+                throw new RestconfDocumentedException(
+                        "Mount point does not exist.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+            }
+
+            final String pathId = pathsIt.next().replaceFirst("/", "");
+            final YangInstanceIdentifier pathYangInstanceIdentifier = IdentifierCodec.deserialize(
+                    pathId, mountPoint.get().getSchemaContext());
+
+            final DataSchemaContextNode<?> child = DataSchemaContextTree.from(
+                    mountPoint.get().getSchemaContext()).getChild(pathYangInstanceIdentifier);
+
+            return new InstanceIdentifierContext<SchemaNode>(
+                    pathYangInstanceIdentifier, child.getDataSchemaNode(), mountPoint.get(),
+                    mountPoint.get().getSchemaContext());
         } else {
-            deserialize = IdentifierCodec.deserialize(identifier, schemaContext);
+            final YangInstanceIdentifier deserialize = IdentifierCodec.deserialize(identifier, schemaContext);
+            final DataSchemaContextNode<?> child = DataSchemaContextTree.from(schemaContext).getChild(deserialize);
+
+            return new InstanceIdentifierContext<SchemaNode>(
+                    deserialize, child.getDataSchemaNode(), null, schemaContext);
         }
-        final DataSchemaContextNode<?> child = DataSchemaContextTree.from(schemaContext).getChild(deserialize);
-        return new InstanceIdentifierContext<SchemaNode>(deserialize, child.getDataSchemaNode(), null, schemaContext);
     }
 
     /**
@@ -170,12 +207,11 @@ public final class ParserIdentifier {
                 pathBuilder.append(current);
             }
             final InstanceIdentifierContext<?> point = ParserIdentifier
-                    .toInstanceIdentifier(pathBuilder.toString(), schemaContext);
-            final DOMMountPoint mountPoint = domMountPointService.getMountPoint(point.getInstanceIdentifier()).get();
+                    .toInstanceIdentifier(pathBuilder.toString(), schemaContext, Optional.of(domMountPointService));
             final String moduleName = RestconfValidation.validateAndGetModulName(componentIter);
             final Date revision = RestconfValidation.validateAndGetRevision(componentIter);
-            final Module module = mountPoint.getSchemaContext().findModuleByName(moduleName, revision);
-            return new SchemaExportContext(mountPoint.getSchemaContext(), module);
+            final Module module = point.getMountPoint().getSchemaContext().findModuleByName(moduleName, revision);
+            return new SchemaExportContext(point.getMountPoint().getSchemaContext(), module);
         }
     }
 }
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.mock;
@@ -24,25 +24,34 @@ import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
-import org.opendaylight.restconf.jersey.providers.AbstractIdentifierAwareJaxRsProvider;
+import org.opendaylight.restconf.RestConnectorProvider;
+import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public abstract class Draft17AbstractBodyReaderTest {
+abstract class AbstractBodyReaderTest {
 
     protected final static ControllerContext controllerContext = ControllerContext.getInstance();
     protected final MediaType mediaType;
     private static Field uriField;
     private static Field requestField;
+    protected final static DOMMountPointServiceHandler mountPointServiceHandler = mock(
+            DOMMountPointServiceHandler.class);
 
-    public Draft17AbstractBodyReaderTest() throws NoSuchFieldException,
-            SecurityException {
+    AbstractBodyReaderTest() throws NoSuchFieldException, IllegalAccessException {
         uriField = AbstractIdentifierAwareJaxRsProvider.class
                 .getDeclaredField("uriInfo");
         uriField.setAccessible(true);
+
         requestField = AbstractIdentifierAwareJaxRsProvider.class
                 .getDeclaredField("request");
         requestField.setAccessible(true);
+
         mediaType = getMediaType();
+
+        final Field mountPointServiceHandlerField = RestConnectorProvider.class.
+                getDeclaredField("mountPointServiceHandler");
+        mountPointServiceHandlerField.setAccessible(true);
+        mountPointServiceHandlerField.set(RestConnectorProvider.class, mountPointServiceHandler);
     }
 
     protected abstract MediaType getMediaType();
@@ -78,12 +87,6 @@ public abstract class Draft17AbstractBodyReaderTest {
         requestField.set(normalizedNodeProvider, request);
     }
 
-    protected static void checkMountPointNormalizedNodeContext(
-            final NormalizedNodeContext nnContext) {
-        checkNormalizedNodeContext(nnContext);
-        assertNotNull(nnContext.getInstanceIdentifierContext().getMountPoint());
-    }
-
     protected static void checkNormalizedNodeContext(
             final NormalizedNodeContext nnContext) {
         assertNotNull(nnContext.getData());
@@ -100,4 +103,10 @@ public abstract class Draft17AbstractBodyReaderTest {
         assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaContext());
         assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaNode());
     }
+
+    protected static void checkPATCHContextMountPoint(final PATCHContext patchContext) {
+        checkPATCHContext(patchContext);
+        assertNotNull(patchContext.getInstanceIdentifierContext().getMountPoint());
+        assertNotNull(patchContext.getInstanceIdentifierContext().getMountPoint().getSchemaContext());
+    }
 }
@@ -1,16 +1,19 @@
-/**
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+import com.google.common.collect.Sets;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
@@ -21,9 +24,10 @@ import java.util.Collection;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.jersey.providers.JsonNormalizedNodeBodyReader;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -34,9 +38,8 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
-import com.google.common.collect.Sets;
 
-public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
+public class JsonBodyReaderTest extends AbstractBodyReaderTest {
 
     private final JsonNormalizedNodeBodyReader jsonBodyReader;
     private static SchemaContext schemaContext;
@@ -52,7 +55,7 @@ public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
         }
     }
 
-    public Draft17JsonBodyReaderTest() throws NoSuchFieldException, SecurityException {
+    public JsonBodyReaderTest() throws Exception {
         super();
         this.jsonBodyReader = new JsonNormalizedNodeBodyReader();
     }
@@ -70,6 +73,7 @@ public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
         testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
         schemaContext = TestRestconfUtils.parseYangSources(testFiles);
         controllerContext.setSchemas(schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
     }
 
     @Test
@@ -79,7 +83,7 @@ public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName());
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.jsonBodyReader, false);
-        final InputStream inputStream = Draft17JsonBodyReaderTest.class
+        final InputStream inputStream = JsonBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/json/jsondata.json");
         final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -96,7 +100,7 @@ public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName);
         final String uri = "instance-identifier-module:cont/cont1";
         mockBodyReader(uri, this.jsonBodyReader, false);
-        final InputStream inputStream = Draft17JsonBodyReaderTest.class
+        final InputStream inputStream = JsonBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
         final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -112,7 +116,7 @@ public class Draft17JsonBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.jsonBodyReader, true);
-        final InputStream inputStream = Draft17JsonBodyReaderTest.class
+        final InputStream inputStream = JsonBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
         final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java
new file mode 100644 (file)
index 0000000..4bce4fd
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.restconf.jersey.providers;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class JsonPATCHBodyReaderMountPointTest extends AbstractBodyReaderTest {
+
+    private final JsonToPATCHBodyReader jsonPATCHBodyReader;
+    private static SchemaContext schemaContext;
+    private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount/";
+
+    public JsonPATCHBodyReaderMountPointTest() throws Exception {
+        super();
+        jsonPATCHBodyReader = new JsonToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(APPLICATION_JSON, null);
+    }
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+
+        final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+
+        when(mountPointServiceHandler.get()).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountPoint));
+        when(mountPoint.getSchemaContext()).thenReturn(schemaContext);
+
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void modulePATCHDataTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdata.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of successful PATCH consisting of create and delete PATCH operations.
+     */
+    @Test
+    public void modulePATCHCreateAndDeleteTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test trying to use PATCH create operation which requires value without value. Test should fail with
+     * {@link RestconfDocumentedException} with error code 400.
+     */
+    @Test
+    public void modulePATCHValueMissingNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataValueMissing.json");
+
+        try {
+            jsonPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to missing value node when attempt to invoke create operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test trying to use value with PATCH delete operation which does not support value. Test should fail with
+     * {@link RestconfDocumentedException} with error code 400.
+     */
+    @Test
+    public void modulePATCHValueNotSupportedNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataValueNotSupported.json");
+
+        try {
+            jsonPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to present value node when attempt to invoke delete operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test using PATCH when target is completely specified in request URI and thus target leaf contains only '/' sign.
+     */
+    @Test
+    public void modulePATCHCompleteTargetInURITest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on list. Test consists of two edit operations - replace and merge.
+     */
+    @Test
+    public void modulePATCHMergeOperationOnListTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnList.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on container. Test consists of two edit operations - create and merge.
+     */
+    @Test
+    public void modulePATCHMergeOperationOnContainerTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+}
@@ -6,27 +6,30 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.InputStream;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.restconf.jersey.providers.JsonToPATCHBodyReader;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public class TestDraft17JsonPATCHBodyReader extends Draft17AbstractBodyReaderTest {
+public class JsonPATCHBodyReaderTest extends AbstractBodyReaderTest {
 
     private final JsonToPATCHBodyReader jsonPATCHBodyReader;
     private static SchemaContext schemaContext;
 
-    public TestDraft17JsonPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+    public JsonPATCHBodyReaderTest() throws Exception {
         super();
         jsonPATCHBodyReader = new JsonToPATCHBodyReader();
     }
@@ -39,6 +42,7 @@ public class TestDraft17JsonPATCHBodyReader extends Draft17AbstractBodyReaderTes
     @BeforeClass
     public static void initialization() {
         schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
         controllerContext.setSchemas(schemaContext);
     }
 
@@ -1,16 +1,19 @@
-/**
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+import com.google.common.collect.Sets;
 import java.io.File;
 import java.io.InputStream;
 import java.net.URI;
@@ -20,9 +23,9 @@ import java.util.Collection;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.jersey.providers.XmlNormalizedNodeBodyReader;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -31,9 +34,8 @@ import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import com.google.common.collect.Sets;
 
-public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
+public class XmlBodyReaderTest extends AbstractBodyReaderTest {
 
     private final XmlNormalizedNodeBodyReader xmlBodyReader;
     private static SchemaContext schemaContext;
@@ -48,7 +50,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
         }
     }
 
-    public Draft17XmlBodyReaderTest() throws NoSuchFieldException, SecurityException {
+    public XmlBodyReaderTest() throws Exception {
         super();
         this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
     }
@@ -65,6 +67,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
         testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
         schemaContext = TestRestconfUtils.parseYangSources(testFiles);
         controllerContext.setSchemas(schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
     }
 
     @Test
@@ -74,7 +77,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName());
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.xmlBodyReader, false);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -91,7 +94,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName);
         final String uri = "instance-identifier-module:cont/cont1";
         mockBodyReader(uri, this.xmlBodyReader, false);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -107,7 +110,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
         final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.xmlBodyReader, true);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -127,7 +130,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
                 .node(contAugmentQName);
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.xmlBodyReader, true);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -151,7 +154,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
                 .node(augmentChoice1QName).node(augChoice2II).node(augmentChoice2QName).node(containerQName);
         final String uri = "instance-identifier-module:cont";
         mockBodyReader(uri, this.xmlBodyReader, true);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -175,7 +178,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
     @Test
     public void findFooContainerUsingNamespaceTest() throws Exception {
         mockBodyReader("", this.xmlBodyReader, true);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
@@ -198,7 +201,7 @@ public class Draft17XmlBodyReaderTest extends Draft17AbstractBodyReaderTest {
     @Test
     public void findBarContainerUsingNamespaceTest() throws Exception {
         mockBodyReader("", this.xmlBodyReader, true);
-        final InputStream inputStream = Draft17XmlBodyReaderTest.class
+        final InputStream inputStream = XmlBodyReaderTest.class
                 .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
         final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
                 inputStream);
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java
new file mode 100644 (file)
index 0000000..9c6205a
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.restconf.jersey.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class XmlPATCHBodyReaderMountPointTest extends AbstractBodyReaderTest {
+
+    private final XmlToPATCHBodyReader xmlPATCHBodyReader;
+    private static SchemaContext schemaContext;
+    private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount/";
+
+    public XmlPATCHBodyReaderMountPointTest() throws Exception {
+        super();
+        xmlPATCHBodyReader = new XmlToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+
+        final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+
+        when(mountPointServiceHandler.get()).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountPoint));
+        when(mountPoint.getSchemaContext()).thenReturn(schemaContext);
+
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdata.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test trying to use PATCH create operation which requires value without value. Error code 400 should be returned.
+     */
+    @Test
+    public void moduleDataValueMissingNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataValueMissing.xml");
+        try {
+            xmlPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to missing value node when attempt to invoke create operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test trying to use value with PATCH delete operation which does not support value. Error code 400 should be
+     * returned.
+     */
+    @Test
+    public void moduleDataNotValueNotSupportedNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml");
+        try {
+            xmlPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to present value node when attempt to invoke delete operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+
+    /**
+     * Test of Yang PATCH with absolute target path.
+     */
+    @Test
+    public void moduleDataAbsoluteTargetPathTest() throws Exception {
+        final String uri = MOUNT_POINT;
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test using PATCH when target is completely specified in request URI and thus target leaf contains only '/' sign.
+     */
+    @Test
+    public void modulePATCHCompleteTargetInURITest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on list. Test consists of two edit operations - replace and merge.
+     */
+    @Test
+    public void moduleDataMergeOperationOnListTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on container. Test consists of two edit operations - create and merge.
+     */
+    @Test
+    public void moduleDataMergeOperationOnContainerTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+}
@@ -6,26 +6,29 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.InputStream;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.restconf.jersey.providers.XmlToPATCHBodyReader;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public class TestDraft17XmlPATCHBodyReader extends Draft17AbstractBodyReaderTest {
+public class XmlPATCHBodyReaderTest extends AbstractBodyReaderTest {
 
     private final XmlToPATCHBodyReader xmlPATCHBodyReader;
     private static SchemaContext schemaContext;
 
-    public TestDraft17XmlPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+    public XmlPATCHBodyReaderTest() throws Exception {
         super();
         xmlPATCHBodyReader = new XmlToPATCHBodyReader();
     }
@@ -36,8 +39,9 @@ public class TestDraft17XmlPATCHBodyReader extends Draft17AbstractBodyReaderTest
     }
 
     @BeforeClass
-    public static void initialization() throws NoSuchFieldException, SecurityException {
+    public static void initialization() {
         schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
         controllerContext.setSchemas(schemaContext);
     }
 
index 998c99c3c53c1b5d32be8a274f8e4bf5f8251b9f..523625ad82b3329db21152591adf08ac5f2749f0 100644 (file)
@@ -556,7 +556,8 @@ public class RestconfModulesServiceTest {
     /**
      * Negative test of getting specific module supported by the mount point when specified mount point is not found
      * (it is not registered in <code>DOMMountPointService</code>). Test is expected to fail with
-     * <code>IllegalStateException</code>.
+     * <code>RestconfDocumentedException</code> and error type, error tag and error status code are compared to
+     * expected values.
      */
     @Test
     public void getModuleMountPointNotFoundNegativeTest() throws Exception {
@@ -564,14 +565,21 @@ public class RestconfModulesServiceTest {
         final RestconfModulesService modulesService = setupNormalMountPoint();
 
         // make test
-        this.thrown.expect(IllegalStateException.class);
-        modulesService.getModule(TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT, null);
+        try {
+            modulesService.getModule(TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT, null);
+            fail("Test should fail due to missing mount point");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error code is not correct", 404, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
     }
 
     /**
      * Negative test of getting all modules supported by the mount point when specified mount point is not found (it
      * is not registered in <code>DOMMountPointService</code>). Test is expected to fail with
-     * <code>IllegalStateException</code>.
+     * <code>RestconfDocumentedException</code> and error type, error tag and error status code are compared to
+     * expected values.
      */
     @Test
     public void getModulesMountPointNotFoundNegativeTest() throws Exception {
@@ -579,7 +587,13 @@ public class RestconfModulesServiceTest {
         final RestconfModulesService modulesService = setupNormalMountPoint();
 
         // make test
-        this.thrown.expect(IllegalStateException.class);
-        modulesService.getModules(NOT_REGISTERED_MOUNT_POINT, null);
+        try {
+            modulesService.getModules(NOT_REGISTERED_MOUNT_POINT, null);
+            fail("Test should fail due to missing mount point");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error code is not correct", 404, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
     }
 }
index 7efab9708a55cc55eca8fcb43340ae59d43b66c1..485b522cf26e6c08ec3f9b631dce48578a2e4fb3 100644 (file)
@@ -11,7 +11,9 @@ package org.opendaylight.restconf.restful.services.impl;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -31,11 +33,13 @@ import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
@@ -46,6 +50,7 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -54,6 +59,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -64,14 +70,18 @@ public class RestconfDataServiceImplTest {
     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
 
     private ContainerNode buildBaseCont;
+    private ContainerNode buildBaseContConfig;
+    private ContainerNode buildBaseContOperational;
     private SchemaContextRef contextRef;
     private YangInstanceIdentifier iidBase;
     private DataSchemaNode schemaNode;
     private RestconfDataServiceImpl dataService;
     private QName baseQName;
-    private QName containerQname;
+    private QName containerPlayerQname;
     private QName leafQname;
-    private ContainerNode buildBaseContToReplace;
+    private ContainerNode buildPlayerCont;
+    private ContainerNode buildLibraryCont;
+    private MapNode buildPlaylistList;
 
     @Mock
     private TransactionChainHandler transactionChainHandler;
@@ -85,52 +95,87 @@ public class RestconfDataServiceImplTest {
     private DOMDataReadOnlyTransaction read;
     @Mock
     private DOMDataWriteTransaction write;
+    @Mock
+    private DOMMountPointServiceHandler mountPointServiceHandler;
+    @Mock
+    private DOMMountPointService mountPointService;
+    @Mock
+    private DOMMountPoint mountPoint;
+    @Mock
+    private DOMDataBroker mountDataBroker;
+    @Mock
+    private DOMTransactionChain transactionChain;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
         baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
-        containerQname = QName.create(baseQName, "player");
+        containerPlayerQname = QName.create(baseQName, "player");
         leafQname = QName.create(baseQName, "gap");
-        LeafNode buildLeaf = Builders.leafBuilder()
+
+        final QName containerLibraryQName = QName.create(baseQName, "library");
+        final QName listPlaylistQName = QName.create(baseQName, "playlist");
+
+        final LeafNode buildLeaf = Builders.leafBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
                 .withValue(0.2)
                 .build();
 
-        ContainerNode buildPlayerCont = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
+        buildPlayerCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQname))
                 .withChild(buildLeaf)
                 .build();
+
+        buildLibraryCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerLibraryQName))
+                .build();
+
+        buildPlaylistList = Builders.mapBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listPlaylistQName))
+                .build();
+
         buildBaseCont = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
                 .withChild(buildPlayerCont)
                 .build();
-        buildLeaf = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
-                .withValue(0.5)
-                .build();
-        buildPlayerCont = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
-                .withChild(buildLeaf)
+
+        // config contains one child the same as in operational and one additional
+        buildBaseContConfig = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withChild(buildPlayerCont)
+                .withChild(buildLibraryCont)
                 .build();
-        buildBaseContToReplace = Builders.containerBuilder()
+
+        // operational contains one child the same as in config and one additional
+        buildBaseContOperational = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
                 .withChild(buildPlayerCont)
+                .withChild(buildPlaylistList)
                 .build();
+
         iidBase = YangInstanceIdentifier.builder()
                 .node(baseQName)
                 .build();
 
         contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
         schemaNode = DataSchemaContextTree.from(contextRef.get()).getChild(iidBase).getDataSchemaNode();
-        MockitoAnnotations.initMocks(this);
+
         final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
 
         schemaContextHandler.onGlobalContextUpdated(contextRef.get());
-        dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler);
+        dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler, mountPointServiceHandler);
         doReturn(domTransactionChain).when(transactionChainHandler).get();
         doReturn(read).when(domTransactionChain).newReadOnlyTransaction();
         doReturn(readWrite).when(domTransactionChain).newReadWriteTransaction();
         doReturn(write).when(domTransactionChain).newWriteOnlyTransaction();
+        doReturn(mountPointService).when(mountPointServiceHandler).get();
+        doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
+        doReturn(contextRef.get()).when(mountPoint).getSchemaContext();
+        doReturn(Optional.of(mountDataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        doReturn(transactionChain).when(mountDataBroker).createTransactionChain(any(TransactionChainListener.class));
+        doReturn(read).when(transactionChain).newReadOnlyTransaction();
+        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
     }
 
     @Test
@@ -145,6 +190,33 @@ public class RestconfDataServiceImplTest {
         assertEquals(buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
     }
 
+    /**
+     * Test read data from mount point when both {@link LogicalDatastoreType#CONFIGURATION} and
+     * {@link LogicalDatastoreType#OPERATIONAL} contains the same data and some additional data to be merged.
+     */
+    @Test
+    public void testReadDataMountPoint() {
+        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContConfig))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContOperational))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, iidBase);
+
+        final Response response = dataService.readData(
+                "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo);
+
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+
+        // response must contain all child nodes from config and operational containers merged in one container
+        final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
+        assertTrue(data instanceof ContainerNode);
+        assertEquals(3, ((ContainerNode) data).getValue().size());
+        assertTrue(((ContainerNode) data).getChild(buildPlayerCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(buildLibraryCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(buildPlaylistList.getIdentifier()).isPresent());
+    }
+
     @Test(expected = RestconfDocumentedException.class)
     public void testReadDataNoData() {
         doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
@@ -152,7 +224,7 @@ public class RestconfDataServiceImplTest {
                 iidBase);
         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL,
                 iidBase);
-        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+        dataService.readData("example-jukebox:jukebox", uriInfo);
     }
 
     @Test
@@ -237,16 +309,29 @@ public class RestconfDataServiceImplTest {
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
     }
 
+    /**
+     * Test of deleting data on mount point
+     */
+    @Test
+    public void testDeleteDataMountPoint() {
+        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
+        final Response response = dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox");
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+    }
+
     @Test
     public void testPatchData() throws Exception {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
         final List<PATCHEntity> entity = new ArrayList<>();
         final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
-                .node(containerQname)
+                .node(containerPlayerQname)
                 .node(leafQname)
                 .build();
         entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
-        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseContToReplace));
+        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
         entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
         final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
 
@@ -264,6 +349,35 @@ public class RestconfDataServiceImplTest {
         assertEquals("replace data", status.getEditCollection().get(1).getEditId());
     }
 
+    @Test
+    public void testPatchDataMountPoint() throws Exception {
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(
+                iidBase, schemaNode, mountPoint, contextRef.get());
+        final List<PATCHEntity> entity = new ArrayList<>();
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
+                .node(containerPlayerQname)
+                .node(leafQname)
+                .build();
+        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
+        final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+
+        final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+        assertTrue(status.isOk());
+        assertEquals(3, status.getEditCollection().size());
+        assertNull(status.getGlobalErrors());
+    }
+
     @Test
     public void testPatchDataDeleteNotExist() throws Exception {
         final Field handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
@@ -277,7 +391,7 @@ public class RestconfDataServiceImplTest {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
         final List<PATCHEntity> entity = new ArrayList<>();
         final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
-                .node(containerQname)
+                .node(containerPlayerQname)
                 .node(leafQname)
                 .build();
         entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
@@ -311,5 +425,4 @@ public class RestconfDataServiceImplTest {
         final String errorMessage = status.getEditCollection().get(2).getEditErrors().get(0).getErrorMessage();
         assertEquals("Data does not exist", errorMessage);
     }
-
 }
\ No newline at end of file
index a5380a3004a55b27d54d132efbe12807e348ed00..368456a97c486c54ae0b2cc2a1af4b89e3ef71f3 100644 (file)
@@ -32,6 +32,8 @@ import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -43,13 +45,10 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * Unit tests for {@link ParserIdentifier}
  */
 public class ParserIdentifierTest {
-    // mount point identifier + expected result
+    // mount point identifier
     private static final String MOUNT_POINT_IDENT =
             "mount-point:mount-container/point-number" + "/" + RestconfConstants.MOUNT;
 
-    private static final String MOUNT_POINT_IDENT_RESULT =
-            "/(mount:point?revision=2016-06-02)mount-container/point-number";
-
     // invalid mount point identifier
     private static final String INVALID_MOUNT_POINT_IDENT =
             "mount-point:point-number" + "/" + RestconfConstants.MOUNT;
@@ -79,6 +78,8 @@ public class ParserIdentifierTest {
 
     // schema context with test modules
     private SchemaContext schemaContext;
+    // contains the same modules but it is different object (it can be compared with equals)
+    private SchemaContext schemaContextOnMountPoint;
 
     private static final String TEST_MODULE_NAME = "test-module";
     private static final String TEST_MODULE_REVISION = "2016-06-02";
@@ -89,8 +90,10 @@ public class ParserIdentifierTest {
     private DOMMountPointService mountPointService;
 
     // mock mount point and mount point service
-    @Mock DOMMountPoint mockMountPoint;
-    @Mock DOMMountPointService mockMountPointService;
+    @Mock
+    private DOMMountPoint mockMountPoint;
+    @Mock
+    private DOMMountPointService mockMountPointService;
 
     @Rule
     public final ExpectedException thrown = ExpectedException.none();
@@ -99,6 +102,7 @@ public class ParserIdentifierTest {
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         schemaContext = TestRestconfUtils.loadSchemaContext("/parser-identifier");
+        schemaContextOnMountPoint = TestRestconfUtils.loadSchemaContext("/parser-identifier");
 
         // create and register mount point
         mountPoint = SimpleDOMMountPoint.create(
@@ -107,7 +111,7 @@ public class ParserIdentifierTest {
                         .node(QName.create("mount:point", "2016-06-02", "point-number"))
                         .build(),
                 ImmutableClassToInstanceMap.copyOf(Maps.newHashMap()),
-                schemaContext
+                schemaContextOnMountPoint
         );
 
         mountPointService = new DOMMountPointServiceImpl();
@@ -129,7 +133,7 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierTest() {
         final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(
-                TEST_IDENT, schemaContext);
+                TEST_IDENT, schemaContext, Optional.absent());
 
         assertEquals("Returned not expected identifier",
                 TEST_IDENT_RESULT, context .getInstanceIdentifier().toString());
@@ -142,7 +146,7 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierOtherModulesTest() {
         final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(
-                TEST_IDENT_OTHERS, schemaContext);
+                TEST_IDENT_OTHERS, schemaContext, Optional.absent());
 
         assertEquals("Returned not expected identifier",
                 TEST_IDENT_OTHERS_RESULT, context.getInstanceIdentifier().toString());
@@ -155,10 +159,16 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierMountPointTest() {
         final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(
-                MOUNT_POINT_IDENT, schemaContext);
+                MOUNT_POINT_IDENT + "/" + TEST_IDENT, schemaContext, Optional.of(mountPointService));
 
         assertEquals("Returned not expected identifier",
-                MOUNT_POINT_IDENT_RESULT, context.getInstanceIdentifier().toString());
+                TEST_IDENT_RESULT.toString(), context.getInstanceIdentifier().toString());
+
+        assertEquals("Mount point not found",
+                mountPoint, context.getMountPoint());
+
+        assertEquals("Schema context from mount point expected",
+                schemaContextOnMountPoint, context.getSchemaContext());
     }
 
     /**
@@ -167,7 +177,8 @@ public class ParserIdentifierTest {
      */
     @Test
     public void toInstanceIdentifierNullIdentifierTest() {
-        final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(null, schemaContext);
+        final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(
+                null, schemaContext, Optional.absent());
         assertEquals("Returned not expected identifier",
                 YangInstanceIdentifier.EMPTY, context.getInstanceIdentifier());
     }
@@ -179,7 +190,7 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierNullSchemaContextNegativeTest() {
         thrown.expect(NullPointerException.class);
-        ParserIdentifier.toInstanceIdentifier(TEST_IDENT, null);
+        ParserIdentifier.toInstanceIdentifier(TEST_IDENT, null, Optional.absent());
     }
 
     /**
@@ -187,19 +198,8 @@ public class ParserIdentifierTest {
      */
     @Test
     public void toInstanceIdentifierEmptyIdentifierTest() {
-        final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier("", schemaContext);
-        assertEquals("Returned not expected identifier",
-                YangInstanceIdentifier.EMPTY, context.getInstanceIdentifier());
-    }
-
-    /**
-     * Api path can be empty. <code>YangInstanceIdentifier.EMPTY</code> is expected to be returned.
-     * Test when identifier contains {@link RestconfConstants#MOUNT}.
-     */
-    @Test
-    public void toInstanceIdentifierEmptyIdentifierMountPointTest() {
         final InstanceIdentifierContext<?> context = ParserIdentifier.toInstanceIdentifier(
-                "" + "/" + RestconfConstants.MOUNT, schemaContext);
+                "", schemaContext, Optional.absent());
         assertEquals("Returned not expected identifier",
                 YangInstanceIdentifier.EMPTY, context.getInstanceIdentifier());
     }
@@ -210,7 +210,7 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierInvalidIdentifierNegativeTest() {
         thrown.expect(IllegalArgumentException.class);
-        ParserIdentifier.toInstanceIdentifier(INVALID_TEST_IDENT, schemaContext);
+        ParserIdentifier.toInstanceIdentifier(INVALID_TEST_IDENT, schemaContext, Optional.absent());
     }
 
     /**
@@ -220,7 +220,48 @@ public class ParserIdentifierTest {
     @Test
     public void toInstanceIdentifierMountPointInvalidIdentifierNegativeTest() {
         thrown.expect(IllegalArgumentException.class);
-        ParserIdentifier.toInstanceIdentifier(INVALID_MOUNT_POINT_IDENT, schemaContext);
+        ParserIdentifier.toInstanceIdentifier(INVALID_MOUNT_POINT_IDENT, schemaContext, Optional.of(mountPointService));
+    }
+
+    /**
+     * Negative test when <code>DOMMountPoint</code> cannot be found. Test is expected to fail with
+     * <code>RestconfDocumentedException</code> error type, error tag and error status code are
+     * compared to expected values.
+     */
+    @Test
+    public void toInstanceIdentifierMissingMountPointNegativeTest() {
+        try {
+            ParserIdentifier.toInstanceIdentifier(
+                    "" + "/" + RestconfConstants.MOUNT, schemaContext, Optional.of(mountPointService));
+            fail("Test should fail due to missing mount point");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Not expected error type",
+                    RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Not expected error tag",
+                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
+            assertEquals("Not expected error status code",
+                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Negative test when <code>{@link DOMMountPointService}</code> is absent. Test is expected to fail with
+     * <code>RestconfDocumentedException</code> error type, error tag and error status code are
+     * compared to expected values.
+     */
+    @Test
+    public void toInstanceIdentifierMissingMountPointServiceNegativeTest() {
+        try {
+            ParserIdentifier.toInstanceIdentifier(RestconfConstants.MOUNT, schemaContext, Optional.absent());
+            fail("Test should fail due to absent mount point service");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Not expected error type",
+                    ErrorType.APPLICATION, e.getErrors().get(0).getErrorType());
+            assertEquals("Not expected error tag",
+                    ErrorTag.OPERATION_FAILED, e.getErrors().get(0).getErrorTag());
+            assertEquals("Not expected error status code",
+                    500, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
     }
 
     /**