* 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.nb.rfc8040.streams;
+package org.opendaylight.restconf.server.mdsal.streams.dtcl;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import com.google.common.collect.ImmutableClassToInstanceMap;
import java.net.URI;
import java.util.UUID;
+import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMNotificationService;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
+import org.opendaylight.restconf.server.mdsal.MdsalRestconfStreamRegistry;
+import org.opendaylight.restconf.server.spi.OperationInput;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.streams.stream.Access;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscription;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionOutput;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
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.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
@ExtendWith(MockitoExtension.class)
-class ListenersBrokerTest {
+class CreateNotificationStreamRpcTest {
private static final EffectiveModelContext SCHEMA_CTX = YangParserTestUtils.parseYangResourceDirectory("/streams");
- private static final URI BASE_URI = URI.create("baseURI");
+ private static final URI RESTCONF_URI = URI.create("/rests");
+ private static final YangInstanceIdentifier TOASTER = YangInstanceIdentifier.of(
+ QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toaster"));
@Mock
private DOMDataBroker dataBroker;
@Mock
private DOMDataTreeChangeService treeChange;
@Mock
- private DOMMountPointService mountPointService;
- @Mock
- private DOMNotificationService notificationService;
- @Mock
private DOMDataTreeWriteTransaction tx;
@Captor
private ArgumentCaptor<YangInstanceIdentifier> pathCaptor;
@Captor
private ArgumentCaptor<NormalizedNode> dataCaptor;
- private ListenersBroker listenersBroker;
private DatabindProvider databindProvider;
+ private CreateDataChangeEventSubscriptionRpc rpc;
+
@BeforeEach
public void before() {
- listenersBroker = new ListenersBroker.ServerSentEvents(dataBroker, notificationService, mountPointService);
databindProvider = () -> DatabindContext.ofModel(SCHEMA_CTX);
- }
- @Test
- void createStreamTest() {
doReturn(ImmutableClassToInstanceMap.of(DOMDataTreeChangeService.class, treeChange))
.when(dataBroker).getExtensions();
+ rpc = new CreateDataChangeEventSubscriptionRpc(new MdsalRestconfStreamRegistry(dataBroker), databindProvider,
+ dataBroker);
+ }
+ @Test
+ void createStreamTest() {
doReturn(tx).when(dataBroker).newWriteOnlyTransaction();
doNothing().when(tx).put(eq(LogicalDatastoreType.OPERATIONAL), pathCaptor.capture(), dataCaptor.capture());
doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
- final var output = assertInstanceOf(ContainerNode.class,
- listenersBroker.createDataChangeNotifiStream(databindProvider, BASE_URI,
- prepareDomPayload("create-data-change-event-subscription", "toaster", "path"), SCHEMA_CTX)
- .getOrThrow()
- .orElse(null));
+ final var output = assertInstanceOf(ContainerNode.class, rpc.invoke(RESTCONF_URI, createInput("path", TOASTER))
+ .getOrThrow().output());
assertEquals(new NodeIdentifier(CreateDataChangeEventSubscriptionOutput.QNAME), output.name());
assertEquals(1, output.size());
.withChild(Builders.mapEntryBuilder()
.withNodeIdentifier(NodeIdentifierWithPredicates.of(Access.QNAME, rcEncoding, "json"))
.withChild(ImmutableNodes.leafNode(rcEncoding, "json"))
- .withChild(ImmutableNodes.leafNode(rcLocation, "rests/streams/json/" + name))
+ .withChild(ImmutableNodes.leafNode(rcLocation, "/rests/streams/json/" + name))
.build())
.withChild(Builders.mapEntryBuilder()
.withNodeIdentifier(NodeIdentifierWithPredicates.of(Access.QNAME, rcEncoding, "xml"))
.withChild(ImmutableNodes.leafNode(rcEncoding, "xml"))
- .withChild(ImmutableNodes.leafNode(rcLocation, "rests/streams/xml/" + name))
+ .withChild(ImmutableNodes.leafNode(rcLocation, "/rests/streams/xml/" + name))
.build())
.build())
.build().prettyTree().toString(), dataCaptor.getValue().prettyTree().toString());
@Test
void createStreamWrongValueTest() {
- final var payload = prepareDomPayload("create-data-change-event-subscription", "String value", "path");
- final var errors = assertThrows(RestconfDocumentedException.class,
- () -> listenersBroker.createDataChangeNotifiStream(databindProvider, BASE_URI, payload, SCHEMA_CTX))
- .getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
- assertEquals(ErrorType.APPLICATION, error.getErrorType());
- assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
- assertEquals("Instance identifier was not normalized correctly", error.getErrorMessage());
+ final var payload = createInput("path", "String value");
+ final var ex = assertThrows(IllegalArgumentException.class, () -> rpc.invoke(RESTCONF_URI, payload));
+ assertEquals("""
+ Bad child leafNode (urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote@2014-01-14)path = \
+ "String value"\
+ """, ex.getMessage());
}
@Test
void createStreamWrongInputRpcTest() {
- final var payload = prepareDomPayload("create-data-change-event-subscription2", "toaster", "path2");
- final var errors = assertThrows(RestconfDocumentedException.class,
- () -> listenersBroker.createDataChangeNotifiStream(databindProvider, BASE_URI, payload, SCHEMA_CTX))
- .getErrors();
+ final var future = rpc.invoke(RESTCONF_URI, createInput(null, null));
+ final var errors = assertThrows(RestconfDocumentedException.class, future::getOrThrow).getErrors();
assertEquals(1, errors.size());
final var error = errors.get(0);
assertEquals(ErrorType.APPLICATION, error.getErrorType());
- assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
- assertEquals("Instance identifier was not normalized correctly", error.getErrorMessage());
+ assertEquals(ErrorTag.MISSING_ELEMENT, error.getErrorTag());
+ assertEquals("missing path", error.getErrorMessage());
}
- private static ContainerNode prepareDomPayload(final String rpcName, final String toasterValue,
- final String inputOutputName) {
- final var rpcModule = SCHEMA_CTX.findModules("sal-remote").iterator().next();
- final QName rpcQName = QName.create(rpcModule.getQNameModule(), rpcName);
-
- ContainerLike containerSchemaNode = null;
- for (final RpcDefinition rpc : rpcModule.getRpcs()) {
- if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
- containerSchemaNode = rpc.getInput();
- break;
- }
+ private OperationInput createInput(final @Nullable String leafName, final Object leafValue) {
+ final var stack = SchemaInferenceStack.of(SCHEMA_CTX);
+ final var rpcStmt = assertInstanceOf(RpcEffectiveStatement.class,
+ stack.enterSchemaTree(CreateDataChangeEventSubscription.QNAME));
+ final var inference = stack.toInference();
+
+ final var builder = Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(rpcStmt.input().argument()));
+ if (leafName != null) {
+ final var lfQName = QName.create(rpcStmt.argument(), leafName);
+ stack.enterDataTree(rpcStmt.input().argument());
+ stack.enterDataTree(lfQName);
+ builder.withChild(ImmutableNodes.leafNode(lfQName, leafValue));
}
- assertNotNull(containerSchemaNode);
-
- final QName lfQName = QName.create(rpcModule.getQNameModule(), inputOutputName);
- assertInstanceOf(LeafSchemaNode.class, containerSchemaNode.dataChildByName(lfQName));
-
- final Object o;
- if ("toaster".equals(toasterValue)) {
- final QName rpcQname = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", toasterValue);
- o = YangInstanceIdentifier.of(rpcQname);
- } else {
- o = toasterValue;
- }
-
- return Builders.containerBuilder()
- .withNodeIdentifier(new NodeIdentifier(containerSchemaNode.getQName()))
- .withChild(ImmutableNodes.leafNode(lfQName, o))
- .build();
+ return new OperationInput(databindProvider.currentContext(), inference, builder.build());
}
}