*/
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.collect.Sets;
-import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
-import java.util.Collections;
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.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.common.errors.RestconfError.ErrorTag;
-import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+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;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
* </ul>
*/
public final class ReadDataTransactionUtil {
- private static final String READ_TYPE_TX = "READ";
-
private ReadDataTransactionUtil() {
- throw new UnsupportedOperationException("Util class.");
- }
-
- /**
- * 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
- checkParametersTypes(uriInfo.getQueryParameters().keySet(),
- RestconfDataServiceConstant.ReadData.CONTENT,
- RestconfDataServiceConstant.ReadData.DEPTH,
- RestconfDataServiceConstant.ReadData.FIELDS, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
-
- // read parameters from URI or set default values
- final List<String> content = uriInfo.getQueryParameters().getOrDefault(
- RestconfDataServiceConstant.ReadData.CONTENT,
- Collections.singletonList(RestconfDataServiceConstant.ReadData.ALL));
- final List<String> depth = uriInfo.getQueryParameters().getOrDefault(
- RestconfDataServiceConstant.ReadData.DEPTH,
- Collections.singletonList(RestconfDataServiceConstant.ReadData.UNBOUNDED));
- final List<String> withDefaults = uriInfo.getQueryParameters().getOrDefault(
- RestconfDataServiceConstant.ReadData.WITH_DEFAULTS,
- Collections.emptyList());
- // fields
- final List<String> fields = uriInfo.getQueryParameters().getOrDefault(
- RestconfDataServiceConstant.ReadData.FIELDS,
- Collections.emptyList());
-
- // parameter can be in URI at most once
- checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT);
- checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH);
- checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS);
- checkParameterCount(fields, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
-
- // check and set content
- final String contentValue = content.get(0);
- if (!contentValue.equals(RestconfDataServiceConstant.ReadData.ALL)) {
- if (!contentValue.equals(RestconfDataServiceConstant.ReadData.CONFIG)
- && !contentValue.equals(RestconfDataServiceConstant.ReadData.NONCONFIG)) {
- throw new RestconfDocumentedException(
- new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
- "Invalid content parameter: " + contentValue, null,
- "The content parameter value must be either config, nonconfig or all (default)"));
- }
- }
-
- builder.setContent(content.get(0));
-
- // 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(RestconfError.ErrorType.PROTOCOL, RestconfError.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(RestconfError.ErrorType.PROTOCOL,
- RestconfError.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();
+ // Hidden on purpose
}
/**
* 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:
- if (withDefa == null) {
- return readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true);
- } else {
- return prepareDataByParamWithDef(
- readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true),
- path, withDefa, ctx);
- }
- case RestconfDataServiceConstant.ReadData.NONCONFIG:
- return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, true);
- case RestconfDataServiceConstant.ReadData.ALL:
+ 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 NONCONFIG:
+ return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path);
default:
throw new RestconfDocumentedException(
- new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
- "Invalid content parameter: " + valueOfContent, null,
+ new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+ "Invalid content parameter: " + content.paramValue(), null,
"The content parameter value must be either config, nonconfig or all (default)"));
}
}
* 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
* @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<YangInstanceIdentifier> fields) {
- switch (valueOfContent) {
- case RestconfDataServiceConstant.ReadData.CONFIG:
- if (withDefa == null) {
- return readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true, fields);
- } else {
- return prepareDataByParamWithDef(
- readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true, fields),
- path, withDefa, ctx);
- }
- case RestconfDataServiceConstant.ReadData.NONCONFIG:
- return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, true, fields);
- case RestconfDataServiceConstant.ReadData.ALL:
+ // 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 NONCONFIG:
+ return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, fields);
default:
- throw new RestconfDocumentedException(new RestconfError(RestconfError.ErrorType.PROTOCOL,
- RestconfError.ErrorTag.INVALID_VALUE, "Invalid content parameter: " + valueOfContent, null,
+ throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+ "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<String> 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<String> usedParameters,
- final @NonNull String... allowedParameters) {
- // FIXME: there should be a speedier way to do this
- final Set<String> notAllowedParameters = Sets.newHashSet(usedParameters);
- notAllowedParameters.removeAll(Sets.newHashSet(allowedParameters));
-
- if (!notAllowedParameters.isEmpty()) {
- throw new RestconfDocumentedException(
- "Not allowed parameters for " + READ_TYPE_TX + " operation: " + notAllowedParameters,
- RestconfError.ErrorType.PROTOCOL,
- RestconfError.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);
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());
}
}
}
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());
}
}
}
* @return {@link NormalizedNode}
*/
static @Nullable NormalizedNode readDataViaTransaction(final @NonNull RestconfStrategy strategy,
- final LogicalDatastoreType store, final YangInstanceIdentifier path,
- final boolean closeTransactionChain) {
- final ListenableFuture<Optional<NormalizedNode>> listenableFuture = strategy.read(store, path);
- return extractReadData(strategy, path, closeTransactionChain, listenableFuture);
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return extractReadData(strategy, path, strategy.read(store, path));
}
/**
*/
private static @Nullable NormalizedNode readDataViaTransaction(final @NonNull RestconfStrategy strategy,
final @NonNull LogicalDatastoreType store, final @NonNull YangInstanceIdentifier path,
- final boolean closeTransactionChain, final @NonNull List<YangInstanceIdentifier> fields) {
- final ListenableFuture<Optional<NormalizedNode>> listenableFuture = strategy.read(store, path, fields);
- return extractReadData(strategy, path, closeTransactionChain, listenableFuture);
+ final @NonNull List<YangInstanceIdentifier> fields) {
+ return extractReadData(strategy, path, strategy.read(store, path, fields));
}
private static NormalizedNode extractReadData(final RestconfStrategy strategy,
- final YangInstanceIdentifier path, final boolean closeTransactionChain,
- final ListenableFuture<Optional<NormalizedNode>> dataFuture) {
+ final YangInstanceIdentifier path, final ListenableFuture<Optional<NormalizedNode>> dataFuture) {
final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
- if (closeTransactionChain) {
- //Method close transactionChain if any
- FutureCallbackTx.addCallback(dataFuture, READ_TYPE_TX, dataFactory, strategy, path);
- } else {
- FutureCallbackTx.addCallback(dataFuture, READ_TYPE_TX, dataFactory);
- }
+ FutureCallbackTx.addCallback(dataFuture, "READ", dataFactory, path);
return dataFactory.build();
}
* @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,
- false);
-
+ final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path);
// PREPARE CONFIG DATA NODE
- final NormalizedNode configDataNode;
- //Here will be closed transactionChain if any
- if (withDefa == null) {
- configDataNode = readDataViaTransaction(
- strategy, LogicalDatastoreType.CONFIGURATION, path, true);
- } else {
- configDataNode = prepareDataByParamWithDef(
- readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true),
- path, withDefa, ctx);
- }
+ final NormalizedNode configDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION,
+ path);
- return mergeConfigAndSTateDataIfNeeded(stateDataNode, configDataNode);
+ return mergeConfigAndSTateDataIfNeeded(stateDataNode,
+ withDefa == null ? configDataNode : prepareDataByParamWithDef(configDataNode, path, withDefa, ctx));
}
/**
* @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<YangInstanceIdentifier> fields) {
// PREPARE STATE DATA NODE
final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path,
- false, fields);
+ fields);
// PREPARE CONFIG DATA NODE
- final NormalizedNode configDataNode;
- //Here will be closed transactionChain if any
- if (withDefa == null) {
- configDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true, fields);
- } else {
- configDataNode = prepareDataByParamWithDef(
- readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path, true, fields),
- path, withDefa, ctx);
- }
-
- return mergeConfigAndSTateDataIfNeeded(stateDataNode, configDataNode);
+ final NormalizedNode configDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path,
+ fields);
+ return mergeConfigAndSTateDataIfNeeded(stateDataNode,
+ withDefa == null ? configDataNode : prepareDataByParamWithDef(configDataNode, path, withDefa, ctx));
}
private static NormalizedNode mergeConfigAndSTateDataIfNeeded(final NormalizedNode stateDataNode,