f39788a72644a46da1185038c9a843e9127ec686
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / server / mdsal / streams / notif / CreateNotificationStreamRpc.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.server.mdsal.streams.notif;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableSet;
13 import java.net.URI;
14 import javax.inject.Inject;
15 import javax.inject.Singleton;
16 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
17 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
18 import org.opendaylight.restconf.common.errors.RestconfFuture;
19 import org.opendaylight.restconf.server.api.OperationsPostResult;
20 import org.opendaylight.restconf.server.spi.DatabindProvider;
21 import org.opendaylight.restconf.server.spi.OperationInput;
22 import org.opendaylight.restconf.server.spi.RestconfStream;
23 import org.opendaylight.restconf.server.spi.RpcImplementation;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionOutput;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateNotificationStream;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateNotificationStreamInput;
27 import org.opendaylight.yangtools.yang.common.ErrorTag;
28 import org.opendaylight.yangtools.yang.common.ErrorType;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
33 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
34 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
35 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
36 import org.osgi.service.component.annotations.Activate;
37 import org.osgi.service.component.annotations.Component;
38 import org.osgi.service.component.annotations.Reference;
39
40 /**
41  * RESTCONF implementation of {@link CreateNotificationStream}.
42  */
43 @Singleton
44 @Component
45 public final class CreateNotificationStreamRpc extends RpcImplementation {
46     private static final NodeIdentifier SAL_REMOTE_OUTPUT_NODEID =
47         NodeIdentifier.create(CreateDataChangeEventSubscriptionOutput.QNAME);
48     private static final NodeIdentifier NOTIFICATIONS =
49         NodeIdentifier.create(QName.create(CreateNotificationStreamInput.QNAME, "notifications").intern());
50     private static final NodeIdentifier STREAM_NAME_NODEID =
51         NodeIdentifier.create(QName.create(CreateDataChangeEventSubscriptionOutput.QNAME, "stream-name").intern());
52
53     private final DatabindProvider databindProvider;
54     private final DOMNotificationService notificationService;
55     private final RestconfStream.Registry streamRegistry;
56
57     @Inject
58     @Activate
59     public CreateNotificationStreamRpc(@Reference final RestconfStream.Registry streamRegistry,
60             @Reference final DatabindProvider databindProvider,
61             @Reference final DOMNotificationService notificationService) {
62         super(CreateNotificationStream.QNAME);
63         this.databindProvider = requireNonNull(databindProvider);
64         this.notificationService = requireNonNull(notificationService);
65         this.streamRegistry = requireNonNull(streamRegistry);
66     }
67
68     @Override
69     public RestconfFuture<OperationsPostResult> invoke(final URI restconfURI, final OperationInput input) {
70         final var body = input.input();
71         final var qnames = ((LeafSetNode<String>) body.getChildByArg(NOTIFICATIONS)).body().stream()
72             .map(LeafSetEntryNode::body)
73             .map(QName::create)
74             .sorted()
75             .collect(ImmutableSet.toImmutableSet());
76
77         final var modelContext = input.databind().modelContext();
78         final var description = new StringBuilder("YANG notifications matching any of {");
79         var haveFirst = false;
80         for (var qname : qnames) {
81             final var optModule = modelContext.findModuleStatement(qname.getModule());
82             if (optModule.isEmpty()) {
83                 return RestconfFuture.failed(new RestconfDocumentedException(qname + " refers to an unknown module",
84                     ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
85             }
86             final var module = optModule.orElseThrow();
87             final var optStmt = module.findSchemaTreeNode(qname);
88             if (optStmt.isEmpty()) {
89                 return RestconfFuture.failed(new RestconfDocumentedException(
90                     qname + " refers to an unknown notification", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
91             }
92             if (!(optStmt.orElseThrow() instanceof NotificationEffectiveStatement)) {
93                 return RestconfFuture.failed(new RestconfDocumentedException(qname + " refers to a non-notification",
94                     ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
95             }
96
97             if (haveFirst) {
98                 description.append(",\n");
99             } else {
100                 haveFirst = true;
101             }
102             description.append("\n  ")
103                 .append(module.argument().getLocalName()).append(':').append(qname.getLocalName());
104         }
105         description.append("\n}");
106
107         return streamRegistry.createStream(restconfURI,
108             new NotificationSource(databindProvider, notificationService, qnames), description.toString())
109             .transform(stream -> input.newOperationOutput(Builders.containerBuilder()
110                 .withNodeIdentifier(SAL_REMOTE_OUTPUT_NODEID)
111                 .withChild(ImmutableNodes.leafNode(STREAM_NAME_NODEID, stream.name()))
112                 .build()));
113     }
114 }