X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=restconf%2Frestconf-nb-rfc8040%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Fnb%2Frfc8040%2Frests%2Futils%2FReadDataTransactionUtil.java;h=76f0d6f1a27f72bdc0767b512a68045977cd0667;hb=01566dd41c162b950cfbbd4eca5a09482ed833c2;hp=30012ecd48de7d2b4c744cb18d44992474aacc82;hpb=4ba21a6334249de2d821e3891d5e19de8baccea1;p=netconf.git diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java index 30012ecd48..76f0d6f1a2 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java @@ -7,32 +7,22 @@ */ package org.opendaylight.restconf.nb.rfc8040.rests.utils; -import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter.parseFieldsParameter; -import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter.parseFieldsPaths; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Ints; import com.google.common.util.concurrent.ListenableFuture; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriInfo; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMTransactionChain; -import org.opendaylight.restconf.common.context.InstanceIdentifierContext; -import org.opendaylight.restconf.common.context.WriterParameters; -import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.nb.rfc8040.ContentParam; +import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam; import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy; -import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.ReadData.WithDefaults; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.common.QName; @@ -83,143 +73,39 @@ import org.opendaylight.yangtools.yang.model.api.RpcDefinition; * */ public final class ReadDataTransactionUtil { - private static final Set ALLOWED_PARAMETERS = Set.of( - RestconfDataServiceConstant.ReadData.CONTENT, - RestconfDataServiceConstant.ReadData.DEPTH, - RestconfDataServiceConstant.ReadData.FIELDS, - RestconfDataServiceConstant.ReadData.WITH_DEFAULTS); - private static final List DEFAULT_CONTENT = List.of(RestconfDataServiceConstant.ReadData.ALL); - private static final List DEFAULT_DEPTH = List.of(RestconfDataServiceConstant.ReadData.UNBOUNDED); - private static final String READ_TYPE_TX = "READ"; - private ReadDataTransactionUtil() { // Hidden on purpose } - /** - * Parse parameters from URI request and check their types and values. - * - * @param identifier {@link InstanceIdentifierContext} - * @param uriInfo URI info - * @return {@link WriterParameters} - */ - public static WriterParameters parseUriParameters(final InstanceIdentifierContext identifier, - final UriInfo uriInfo) { - final WriterParametersBuilder builder = new WriterParametersBuilder(); - if (uriInfo == null) { - return builder.build(); - } - - // check only allowed parameters - final MultivaluedMap queryParams = uriInfo.getQueryParameters(); - checkParametersTypes(queryParams.keySet(), ALLOWED_PARAMETERS); - - // read parameters from URI or set default values - final List content = queryParams.getOrDefault( - RestconfDataServiceConstant.ReadData.CONTENT, DEFAULT_CONTENT); - final List depth = queryParams.getOrDefault( - RestconfDataServiceConstant.ReadData.DEPTH, DEFAULT_DEPTH); - final List withDefaults = queryParams.getOrDefault( - RestconfDataServiceConstant.ReadData.WITH_DEFAULTS, List.of()); - // fields - final List fields = queryParams.getOrDefault(RestconfDataServiceConstant.ReadData.FIELDS, List.of()); - - // parameter can be in URI at most once - checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT); - checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH); - checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS); - checkParameterCount(withDefaults, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS); - - // check and set content - final String contentValue = content.get(0); - switch (contentValue) { - case RestconfDataServiceConstant.ReadData.ALL: - case RestconfDataServiceConstant.ReadData.CONFIG: - case RestconfDataServiceConstant.ReadData.NONCONFIG: - // FIXME: we really want to have a proper enumeration for this field - builder.setContent(contentValue); - break; - default: - throw new RestconfDocumentedException( - new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, - "Invalid content parameter: " + contentValue, null, - "The content parameter value must be either config, nonconfig or all (default)")); - } - - // check and set depth - if (!depth.get(0).equals(RestconfDataServiceConstant.ReadData.UNBOUNDED)) { - final Integer value = Ints.tryParse(depth.get(0)); - - if (value == null || value < RestconfDataServiceConstant.ReadData.MIN_DEPTH - || value > RestconfDataServiceConstant.ReadData.MAX_DEPTH) { - throw new RestconfDocumentedException( - new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, - "Invalid depth parameter: " + depth, null, - "The depth parameter must be an integer between 1 and 65535 or \"unbounded\"")); - } else { - builder.setDepth(value); - } - } - - // check and set fields - if (!fields.isEmpty()) { - if (identifier.getMountPoint() != null) { - builder.setFieldPaths(parseFieldsPaths(identifier, fields.get(0))); - } else { - builder.setFields(parseFieldsParameter(identifier, fields.get(0))); - } - } - - // check and set withDefaults parameter - if (!withDefaults.isEmpty()) { - final String str = withDefaults.get(0); - final WithDefaults val = WithDefaults.forValue(str); - if (val == null) { - throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, - "Invalid with-defaults parameter: " + str, null, - "The with-defaults parameter must be a string in " + WithDefaults.possibleValues())); - } - - switch (val) { - case REPORT_ALL: - break; - case REPORT_ALL_TAGGED: - builder.setTagged(true); - break; - default: - builder.setWithDefault(val.value()); - } - } - return builder.build(); - } - /** * Read specific type of data from data store via transaction. Close {@link DOMTransactionChain} if any * inside of object {@link RestconfStrategy} provided as a parameter. * - * @param valueOfContent type of data to read (config, state, all) + * @param content type of data to read (config, state, all) * @param path the path to read * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations * @param withDefa value of with-defaults parameter * @param ctx schema context * @return {@link NormalizedNode} */ - public static @Nullable NormalizedNode readData(final @NonNull String valueOfContent, + public static @Nullable NormalizedNode readData(final @NonNull ContentParam content, final @NonNull YangInstanceIdentifier path, final @NonNull RestconfStrategy strategy, - final String withDefa, final EffectiveModelContext ctx) { - switch (valueOfContent) { - case RestconfDataServiceConstant.ReadData.CONFIG: + final WithDefaultsParam withDefa, + final EffectiveModelContext ctx) { + // FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException + switch (content) { + case ALL: + return readAllData(strategy, path, withDefa, ctx); + case CONFIG: final NormalizedNode read = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path); return withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa, ctx); - case RestconfDataServiceConstant.ReadData.NONCONFIG: + case NONCONFIG: return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path); - case RestconfDataServiceConstant.ReadData.ALL: - return readAllData(strategy, path, withDefa, ctx); default: throw new RestconfDocumentedException( new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, - "Invalid content parameter: " + valueOfContent, null, + "Invalid content parameter: " + content.paramValue(), null, "The content parameter value must be either config, nonconfig or all (default)")); } } @@ -228,7 +114,7 @@ public final class ReadDataTransactionUtil { * Read specific type of data from data store via transaction with specified subtrees that should only be read. * Close {@link DOMTransactionChain} inside of object {@link RestconfStrategy} provided as a parameter. * - * @param valueOfContent type of data to read (config, state, all) + * @param content type of data to read (config, state, all) * @param path the parent path to read * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations * @param withDefa value of with-defaults parameter @@ -236,72 +122,39 @@ public final class ReadDataTransactionUtil { * @param fields paths to selected subtrees which should be read, relative to to the parent path * @return {@link NormalizedNode} */ - public static @Nullable NormalizedNode readData(final @NonNull String valueOfContent, + public static @Nullable NormalizedNode readData(final @NonNull ContentParam content, final @NonNull YangInstanceIdentifier path, final @NonNull RestconfStrategy strategy, - final @Nullable String withDefa, @NonNull final EffectiveModelContext ctx, + final @Nullable WithDefaultsParam withDefa, @NonNull final EffectiveModelContext ctx, final @NonNull List fields) { - switch (valueOfContent) { - case RestconfDataServiceConstant.ReadData.CONFIG: + // FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException + switch (content) { + case ALL: + return readAllData(strategy, path, withDefa, ctx, fields); + case CONFIG: final NormalizedNode read = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, fields); return withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa, ctx); - case RestconfDataServiceConstant.ReadData.NONCONFIG: + case NONCONFIG: return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, fields); - case RestconfDataServiceConstant.ReadData.ALL: - return readAllData(strategy, path, withDefa, ctx, fields); default: throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, - "Invalid content parameter: " + valueOfContent, null, + "Invalid content parameter: " + content.paramValue(), null, "The content parameter value must be either config, nonconfig or all (default)")); } } - /** - * Check if URI does not contain value for the same parameter more than once. - * - * @param parameterValues URI parameter values - * @param parameterName URI parameter name - */ - @VisibleForTesting - static void checkParameterCount(final @NonNull List parameterValues, final @NonNull String parameterName) { - if (parameterValues.size() > 1) { - throw new RestconfDocumentedException( - "Parameter " + parameterName + " can appear at most once in request URI", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } - } - - /** - * Check if URI does not contain not allowed parameters for specified operation. - * - * @param usedParameters parameters used in URI request - * @param allowedParameters allowed parameters for operation - */ - @VisibleForTesting - static void checkParametersTypes(final @NonNull Set usedParameters, - final @NonNull Set allowedParameters) { - if (!allowedParameters.containsAll(usedParameters)) { - final Set notAllowedParameters = usedParameters.stream() - .filter(param -> !allowedParameters.contains(param)) - .collect(Collectors.toSet()); - throw new RestconfDocumentedException( - "Not allowed parameters for " + READ_TYPE_TX + " operation: " + notAllowedParameters, - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } - } - private static NormalizedNode prepareDataByParamWithDef(final NormalizedNode result, - final YangInstanceIdentifier path, final String withDefa, final EffectiveModelContext ctx) { + final YangInstanceIdentifier path, final WithDefaultsParam withDefa, final EffectiveModelContext ctx) { boolean trim; switch (withDefa) { - case "trim": + case TRIM: trim = true; break; - case "explicit": + case EXPLICIT: trim = false; break; default: - throw new RestconfDocumentedException(""); + throw new RestconfDocumentedException("Unsupported with-defaults value " + withDefa.paramValue()); } final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx); @@ -346,18 +199,14 @@ public final class ReadDataTransactionUtil { if (keys.contains(child.getIdentifier().getNodeType())) { leafBuilder.withValue(((LeafNode) child).body()); builder.withChild(leafBuilder.build()); - } else { - if (trim) { - if (defaultVal == null || !defaultVal.equals(nodeVal)) { - leafBuilder.withValue(((LeafNode) child).body()); - builder.withChild(leafBuilder.build()); - } - } else { - if (defaultVal != null && defaultVal.equals(nodeVal)) { - leafBuilder.withValue(((LeafNode) child).body()); - builder.withChild(leafBuilder.build()); - } + } else if (trim) { + if (defaultVal == null || !defaultVal.equals(nodeVal)) { + leafBuilder.withValue(((LeafNode) child).body()); + builder.withChild(leafBuilder.build()); } + } else if (defaultVal != null && defaultVal.equals(nodeVal)) { + leafBuilder.withValue(((LeafNode) child).body()); + builder.withChild(leafBuilder.build()); } } } @@ -404,11 +253,9 @@ public final class ReadDataTransactionUtil { leafBuilder.withValue(((LeafNode) child).body()); builder.withChild(leafBuilder.build()); } - } else { - if (defaultVal != null && defaultVal.equals(nodeVal)) { - leafBuilder.withValue(((LeafNode) child).body()); - builder.withChild(leafBuilder.build()); - } + } else if (defaultVal != null && defaultVal.equals(nodeVal)) { + leafBuilder.withValue(((LeafNode) child).body()); + builder.withChild(leafBuilder.build()); } } } @@ -449,7 +296,7 @@ public final class ReadDataTransactionUtil { private static NormalizedNode extractReadData(final RestconfStrategy strategy, final YangInstanceIdentifier path, final ListenableFuture> dataFuture) { final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory(); - FutureCallbackTx.addCallback(dataFuture, READ_TYPE_TX, dataFactory, path); + FutureCallbackTx.addCallback(dataFuture, "READ", dataFactory, path); return dataFactory.build(); } @@ -463,7 +310,7 @@ public final class ReadDataTransactionUtil { * @return {@link NormalizedNode} */ private static @Nullable NormalizedNode readAllData(final @NonNull RestconfStrategy strategy, - final YangInstanceIdentifier path, final String withDefa, final EffectiveModelContext ctx) { + final YangInstanceIdentifier path, final WithDefaultsParam withDefa, final EffectiveModelContext ctx) { // PREPARE STATE DATA NODE final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path); // PREPARE CONFIG DATA NODE @@ -486,7 +333,7 @@ public final class ReadDataTransactionUtil { * @return {@link NormalizedNode} */ private static @Nullable NormalizedNode readAllData(final @NonNull RestconfStrategy strategy, - final @NonNull YangInstanceIdentifier path, final @Nullable String withDefa, + final @NonNull YangInstanceIdentifier path, final @Nullable WithDefaultsParam withDefa, final @NonNull EffectiveModelContext ctx, final @NonNull List fields) { // PREPARE STATE DATA NODE final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path,