X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Frestconf-nb-rfc8040%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Fnb%2Frfc8040%2Frests%2Fservices%2Fimpl%2FRestconfDataServiceImpl.java;h=8257b6394e0cc986552805cadb9b8211ff8e2582;hb=365a4ee24f4760a4e09b72d4959f618479dccf2a;hp=36056992fc62f6b653b28957ff45cfbd78e7b6ce;hpb=2d057c4c6b294f2b62d9a0ed04f3a465fa688500;p=netconf.git diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java index 36056992fc..8257b6394e 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java @@ -16,7 +16,6 @@ import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsCo import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.MoreExecutors; import java.net.URI; @@ -26,7 +25,6 @@ import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -34,7 +32,6 @@ import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMActionException; import org.opendaylight.mdsal.dom.api.DOMActionResult; @@ -45,15 +42,15 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations; import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.mdsal.dom.api.DOMMountPointService; -import org.opendaylight.mdsal.dom.api.DOMSchemaService; import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchStatusContext; -import org.opendaylight.restconf.nb.rfc8040.InsertParameter; -import org.opendaylight.restconf.nb.rfc8040.PointParameter; +import org.opendaylight.restconf.nb.rfc8040.ReadDataParams; import org.opendaylight.restconf.nb.rfc8040.Rfc8040; +import org.opendaylight.restconf.nb.rfc8040.WriteDataParams; +import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload; import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters; @@ -72,12 +69,10 @@ import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListen import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; -import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; @@ -100,17 +95,6 @@ import org.slf4j.LoggerFactory; */ @Path("/") public class RestconfDataServiceImpl implements RestconfDataService { - // FIXME: we should be able to interpret 'point' and refactor this class into a behavior - private static final class QueryParams implements Immutable { - final @Nullable PointParameter point; - final @Nullable InsertParameter insert; - - QueryParams(final @Nullable InsertParameter insert, final @Nullable PointParameter point) { - this.insert = insert; - this.point = point; - } - } - private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss"); private static final QName NETCONF_BASE_QNAME = SchemaContext.NAME; @@ -144,11 +128,11 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public Response readData(final String identifier, final UriInfo uriInfo) { + final ReadDataParams readParams = QueryParams.newReadDataParams(uriInfo); + final EffectiveModelContext schemaContextRef = schemaContextHandler.get(); - final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( + final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( identifier, schemaContextRef, Optional.of(mountPointService)); - final QueryParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo); - final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); // FIXME: this looks quite crazy, why do we even have it? @@ -157,15 +141,16 @@ public class RestconfDataServiceImpl implements RestconfDataService { createAllYangNotificationStreams(schemaContextRef, uriInfo); } - final List fieldPaths = parameters.getFieldPaths(); + final QueryParameters queryParams = QueryParams.newQueryParameters(readParams, instanceIdentifier); + final List fieldPaths = queryParams.fieldPaths(); final RestconfStrategy strategy = getRestconfStrategy(mountPoint); final NormalizedNode node; if (fieldPaths != null && !fieldPaths.isEmpty()) { - node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(), - strategy, parameters.getWithDefault(), schemaContextRef, fieldPaths); + node = ReadDataTransactionUtil.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(), + strategy, readParams.withDefaults(), schemaContextRef, fieldPaths); } else { - node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(), - strategy, parameters.getWithDefault(), schemaContextRef); + node = ReadDataTransactionUtil.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(), + strategy, readParams.withDefaults(), schemaContextRef); } // FIXME: this is utter craziness, refactor it properly! @@ -181,19 +166,19 @@ public class RestconfDataServiceImpl implements RestconfDataService { ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); } - switch (parameters.getContent()) { + switch (readParams.content()) { case ALL: case CONFIG: final QName type = node.getIdentifier().getNodeType(); return Response.status(Status.OK) - .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, parameters)) + .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, queryParams)) .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null) + "-" + type.getLocalName() + '"') .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC()))) .build(); default: return Response.status(Status.OK) - .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, parameters)) + .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, queryParams)) .build(); } } @@ -230,74 +215,16 @@ public class RestconfDataServiceImpl implements RestconfDataService { public Response putData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - final QueryParams checkedParms = checkQueryParameters(uriInfo); + final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo); - final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); - validInputData(iid.getSchemaNode(), payload); + validInputData(iid.getSchemaNode() != null, payload); validTopLevelNodeName(iid.getInstanceIdentifier(), payload); validateListKeysEqualityInPayloadAndUri(payload); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final EffectiveModelContext ref = mountPoint == null - ? schemaContextHandler.get() : modelContext(mountPoint); - - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point); - } - - private static QueryParams checkQueryParameters(final UriInfo uriInfo) { - boolean insertUsed = false; - boolean pointUsed = false; - InsertParameter insert = null; - PointParameter point = null; - - for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { - final String uriName = entry.getKey(); - if (InsertParameter.uriName().equals(uriName)) { - if (insertUsed) { - throw new RestconfDocumentedException("Insert parameter can be used only once.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - - insertUsed = true; - final String str = entry.getValue().get(0); - insert = InsertParameter.forUriValue(str); - if (insert == null) { - throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - } else if (PointParameter.uriName().equals(uriName)) { - if (pointUsed) { - throw new RestconfDocumentedException("Point parameter can be used only once.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - - pointUsed = true; - point = PointParameter.forUriValue(entry.getValue().get(0)); - } else { - throw new RestconfDocumentedException("Bad parameter for post: " + uriName, - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - } - - checkQueryParams(insertUsed, pointUsed, insert); - return new QueryParams(insert, point); - } - - private static void checkQueryParams(final boolean insertUsed, final boolean pointUsed, - final InsertParameter insert) { - if (pointUsed) { - if (!insertUsed) { - throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - if (insert != InsertParameter.BEFORE && insert != InsertParameter.AFTER) { - throw new RestconfDocumentedException( - "Point parameter can be used only with 'after' or 'before' values of Insert parameter.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - } + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PutDataTransactionUtil.putData(payload, iid.getSchemaContext(), strategy, params); } @Override @@ -308,20 +235,19 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public Response postData(final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - if (payload.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition) { + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + if (iid.getSchemaNode() instanceof ActionDefinition) { return invokeAction(payload); } - final QueryParams checkedParms = checkQueryParameters(uriInfo); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PostDataTransactionUtil.postData(uriInfo, payload, strategy, - getSchemaContext(mountPoint), checkedParms.insert, checkedParms.point); + final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PostDataTransactionUtil.postData(uriInfo, payload, strategy, iid.getSchemaContext(), params); } @Override public Response deleteData(final String identifier) { - final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( + final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( identifier, schemaContextHandler.get(), Optional.of(mountPointService)); final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); @@ -336,32 +262,24 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) { - final DOMMountPoint mountPoint = RestconfDocumentedException.throwIfNull(context, + final InstanceIdentifierContext iid = RestconfDocumentedException.throwIfNull(context, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "No patch documented provided") - .getInstanceIdentifierContext().getMountPoint(); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PatchDataTransactionUtil.patchData(context, strategy, getSchemaContext(mountPoint)); + .getInstanceIdentifierContext(); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PatchDataTransactionUtil.patchData(context, strategy, iid.getSchemaContext()); } @Override public Response patchData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); - validInputData(iid.getSchemaNode(), payload); + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + validInputData(iid.getSchemaNode() != null, payload); validTopLevelNodeName(iid.getInstanceIdentifier(), payload); validateListKeysEqualityInPayloadAndUri(payload); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final EffectiveModelContext ref = mountPoint == null - ? schemaContextHandler.get() : modelContext(mountPoint); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - - return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref); - } - - private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) { - return mountPoint == null ? schemaContextHandler.get() : modelContext(mountPoint); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PlainPatchDataTransactionUtil.patchData(payload, strategy, iid.getSchemaContext()); } // FIXME: why is this synchronized? @@ -384,10 +302,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { * @return {@link NormalizedNodePayload} wrapped in {@link Response} */ public Response invokeAction(final NormalizedNodePayload payload) { - final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); - final DOMMountPoint mountPoint = context.getMountPoint(); - final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath() - .getPathFromRoot())); + final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier(); final NormalizedNode data = payload.getData(); @@ -396,25 +311,19 @@ public class RestconfDataServiceImpl implements RestconfDataService { ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } + final DOMMountPoint mountPoint = context.getMountPoint(); + final Absolute schemaPath = context.inference().toSchemaInferenceStack().toSchemaNodeIdentifier(); final DOMActionResult response; - final EffectiveModelContext schemaContextRef; if (mountPoint != null) { response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint); - schemaContextRef = modelContext(mountPoint); } else { response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService); - schemaContextRef = schemaContextHandler.get(); } final DOMActionResult result = checkActionResponse(response); - ActionDefinition resultNodeSchema = null; ContainerNode resultData = null; if (result != null) { - final Optional optOutput = result.getOutput(); - if (optOutput.isPresent()) { - resultData = optOutput.get(); - resultNodeSchema = (ActionDefinition) context.getSchemaNode(); - } + resultData = result.getOutput().orElse(null); } if (resultData != null && resultData.isEmpty()) { @@ -422,13 +331,10 @@ public class RestconfDataServiceImpl implements RestconfDataService { } return Response.status(Status.OK) - .entity(NormalizedNodePayload.ofNullable( - new InstanceIdentifierContext<>(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef), - resultData)) + .entity(NormalizedNodePayload.ofNullable(context, resultData)) .build(); } - /** * Invoking Action via mount point. * @@ -458,8 +364,8 @@ public class RestconfDataServiceImpl implements RestconfDataService { return RestconfInvokeOperationsServiceImpl.checkedGet(Futures.catching(actionService.invokeAction( schemaPath, new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, yangIId.getParent()), data), DOMActionException.class, - cause -> new SimpleDOMActionResult(ImmutableList.of(RpcResultBuilder.newError( - RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))), + cause -> new SimpleDOMActionResult(List.of(RpcResultBuilder.newError( + ErrorType.RPC, ErrorTag.OPERATION_FAILED, cause.getMessage()))), MoreExecutors.directExecutor())); } @@ -488,16 +394,20 @@ public class RestconfDataServiceImpl implements RestconfDataService { } /** - * Valid input data with {@link SchemaNode}. + * Valid input data based on presence of a schema node. * - * @param schemaNode {@link SchemaNode} + * @param haveSchemaNode true if there is an underlying schema node * @param payload input data */ @VisibleForTesting - public static void validInputData(final SchemaNode schemaNode, final NormalizedNodePayload payload) { - if (schemaNode != null && payload.getData() == null) { - throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); - } else if (schemaNode == null && payload.getData() != null) { + public static void validInputData(final boolean haveSchemaNode, final NormalizedNodePayload payload) { + final boolean haveData = payload.getData() != null; + if (haveSchemaNode) { + if (!haveData) { + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + } else if (haveData) { throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } } @@ -536,7 +446,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { */ @VisibleForTesting public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodePayload payload) { - final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument(); final SchemaNode schemaNode = iiWithData.getSchemaNode(); final NormalizedNode data = payload.getData(); @@ -567,10 +477,4 @@ public class RestconfDataServiceImpl implements RestconfDataService { } } } - - private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { - return mountPoint.getService(DOMSchemaService.class) - .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) - .orElse(null); - } }