*/
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.Arrays;
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.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.ContentParameter;
-import org.opendaylight.restconf.nb.rfc8040.DepthParameter;
-import org.opendaylight.restconf.nb.rfc8040.FieldsParameter;
-import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParameter;
-import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
+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.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
* </ul>
*/
public final class ReadDataTransactionUtil {
- // depth values
- // FIXME: these are known to DepthParameter
- private static final String UNBOUNDED = "unbounded";
- private static final int MIN_DEPTH = 1;
- private static final int MAX_DEPTH = 65535;
-
- private static final Set<String> ALLOWED_PARAMETERS = Set.of(
- ContentParameter.uriName(),
- DepthParameter.uriName(),
- FieldsParameter.uriName(),
- WithDefaultsParameter.uriName());
- private static final List<String> DEFAULT_CONTENT = List.of(ContentParameter.ALL.uriValue());
- private static final List<String> DEFAULT_DEPTH = List.of(UNBOUNDED);
- private static final List<String> POSSIBLE_CONTENT = Arrays.stream(ContentParameter.values())
- .map(ContentParameter::uriValue)
- .collect(Collectors.toUnmodifiableList());
- private static final List<String> POSSIBLE_WITH_DEFAULTS = Arrays.stream(WithDefaultsParameter.values())
- .map(WithDefaultsParameter::uriValue)
- .collect(Collectors.toUnmodifiableList());
-
- 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 QueryParameters}
- */
- public static QueryParameters parseUriParameters(final InstanceIdentifierContext<?> identifier,
- final UriInfo uriInfo) {
- final QueryParameters.Builder builder = QueryParameters.builder();
- if (uriInfo == null) {
- return builder.build();
- }
-
- // check only allowed parameters
- final MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
- checkParametersTypes(queryParams.keySet(), ALLOWED_PARAMETERS);
-
- // read parameters from URI or set default values
- final List<String> content = queryParams.getOrDefault(ContentParameter.uriName(), DEFAULT_CONTENT);
- final List<String> depth = queryParams.getOrDefault(DepthParameter.uriName(), DEFAULT_DEPTH);
- final List<String> withDefaults = queryParams.getOrDefault(WithDefaultsParameter.uriName(), List.of());
- // fields
- final List<String> fields = queryParams.getOrDefault(FieldsParameter.uriName(), List.of());
-
- // parameter can be in URI at most once
- checkParameterCount(content, ContentParameter.uriName());
- checkParameterCount(depth, DepthParameter.uriName());
- checkParameterCount(fields, FieldsParameter.uriName());
- checkParameterCount(withDefaults, WithDefaultsParameter.uriName());
-
- // check and set content
- final String contentValueStr = content.get(0);
- builder.setContent(RestconfDocumentedException.throwIfNull(
- ContentParameter.forUriValue(contentValueStr), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
- "Invalid content parameter: %s, allowed values are %s", contentValueStr, POSSIBLE_CONTENT));
-
- // check and set depth
- if (!depth.get(0).equals(UNBOUNDED)) {
- final Integer value = Ints.tryParse(depth.get(0));
-
- if (value == null || value < MIN_DEPTH || value > 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 WithDefaultsParameter val = WithDefaultsParameter.forUriValue(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 " + POSSIBLE_WITH_DEFAULTS));
- }
-
- switch (val) {
- case REPORT_ALL:
- break;
- case REPORT_ALL_TAGGED:
- builder.setTagged(true);
- break;
- default:
- builder.setWithDefault(val);
- }
- }
- 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 ctx schema context
* @return {@link NormalizedNode}
*/
- public static @Nullable NormalizedNode readData(final @NonNull ContentParameter content,
+ public static @Nullable NormalizedNode readData(final @NonNull ContentParam content,
final @NonNull YangInstanceIdentifier path,
final @NonNull RestconfStrategy strategy,
- final WithDefaultsParameter withDefa,
+ final WithDefaultsParam withDefa,
final EffectiveModelContext ctx) {
// FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException
switch (content) {
default:
throw new RestconfDocumentedException(
new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
- "Invalid content parameter: " + content.uriValue(), null,
+ "Invalid content parameter: " + content.paramValue(), null,
"The content parameter value must be either config, nonconfig or all (default)"));
}
}
* @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 ContentParameter content,
+ public static @Nullable NormalizedNode readData(final @NonNull ContentParam content,
final @NonNull YangInstanceIdentifier path, final @NonNull RestconfStrategy strategy,
- final @Nullable WithDefaultsParameter withDefa, @NonNull final EffectiveModelContext ctx,
+ final @Nullable WithDefaultsParam withDefa, @NonNull final EffectiveModelContext ctx,
final @NonNull List<YangInstanceIdentifier> fields) {
// FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException
switch (content) {
return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, fields);
default:
throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
- "Invalid content parameter: " + content.uriValue(), 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<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 Set<String> allowedParameters) {
- if (!allowedParameters.containsAll(usedParameters)) {
- final Set<String> 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 WithDefaultsParameter withDefa, final EffectiveModelContext ctx) {
+ final YangInstanceIdentifier path, final WithDefaultsParam withDefa, final EffectiveModelContext ctx) {
boolean trim;
switch (withDefa) {
case TRIM:
trim = false;
break;
default:
- throw new RestconfDocumentedException("Unsupported with-defaults value " + withDefa.uriValue());
+ 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());
}
}
}
private static NormalizedNode extractReadData(final RestconfStrategy strategy,
final YangInstanceIdentifier path, final ListenableFuture<Optional<NormalizedNode>> dataFuture) {
final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
- FutureCallbackTx.addCallback(dataFuture, READ_TYPE_TX, dataFactory, path);
+ 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 WithDefaultsParameter 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
* @return {@link NormalizedNode}
*/
private static @Nullable NormalizedNode readAllData(final @NonNull RestconfStrategy strategy,
- final @NonNull YangInstanceIdentifier path, final @Nullable WithDefaultsParameter 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,