import static java.util.Objects.requireNonNull;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.ParseException;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.api.MediaTypes;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfError;
import org.opendaylight.restconf.common.errors.RestconfFuture;
import org.opendaylight.restconf.common.patch.PatchStatusContext;
import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
import org.opendaylight.restconf.server.api.DataPutResult;
+import org.opendaylight.restconf.server.api.ModulesGetResult;
import org.opendaylight.restconf.server.api.OperationsGetResult;
import org.opendaylight.restconf.server.api.RestconfServer;
import org.opendaylight.restconf.server.spi.OperationOutput;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Suspended final AsyncResponse ar) {
server.dataDELETE(identifier).addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- protected Response transform(final Empty result) {
+ Response transform(final Empty result) {
return Response.noContent().build();
}
});
final ReadDataParams readParams, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- protected Response transform(final NormalizedNodePayload result) {
+ Response transform(final NormalizedNodePayload result) {
return switch (readParams.content()) {
case ALL, CONFIG -> {
final var type = result.data().name().getNodeType();
private static void completeDataPATCH(final RestconfFuture<Empty> future, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- protected Response transform(final Empty result) {
+ Response transform(final Empty result) {
return Response.ok().build();
}
});
private static void completeDataYangPATCH(final RestconfFuture<PatchStatusContext> future, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- protected Response transform(final PatchStatusContext result) {
+ Response transform(final PatchStatusContext result) {
return Response.status(statusOf(result)).entity(result).build();
}
final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<DataPostResult>(ar) {
@Override
- protected Response transform(final DataPostResult result) {
+ Response transform(final DataPostResult result) {
if (result instanceof CreateResource createResource) {
return Response.created(uriInfo.getBaseUriBuilder()
.path("data")
private static void completeDataPUT(final RestconfFuture<DataPutResult> future, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- protected Response transform(final DataPutResult result) {
+ Response transform(final DataPutResult result) {
return switch (result) {
// Note: no Location header, as it matches the request path
case CREATED -> Response.status(Status.CREATED).build();
final Function<OperationsGetResult, String> toString) {
future.addCallback(new JaxRsRestconfCallback<OperationsGetResult>(ar) {
@Override
- protected Response transform(final OperationsGetResult result) {
+ Response transform(final OperationsGetResult result) {
return Response.ok().entity(toString.apply(result)).build();
}
});
server.operationsPOST(uriInfo.getBaseUri(), identifier, body)
.addCallback(new JaxRsRestconfCallback<OperationOutput>(ar) {
@Override
- protected Response transform(final OperationOutput result) {
+ Response transform(final OperationOutput result) {
final var body = result.output();
return body == null ? Response.noContent().build()
: Response.ok().entity(new NormalizedNodePayload(result.operation(), body)).build();
public void yangLibraryVersionGET(@Suspended final AsyncResponse ar) {
server.yangLibraryVersionGET().addCallback(new JaxRsRestconfCallback<NormalizedNodePayload>(ar) {
@Override
- protected Response transform(final NormalizedNodePayload result) {
+ Response transform(final NormalizedNodePayload result) {
return Response.ok().entity(result).build();
}
});
}
+
+ // FIXME: References to these resources are generated by our yang-library implementation. That means:
+ // - We really need to formalize the parameter structure so we get some help from JAX-RS during matching
+ // of three things:
+ // - optional yang-ext:mount prefix(es)
+ // - mandatory module name
+ // - optional module revision
+ // - We really should use /yang-library-module/{name}(/{revision})?
+ // - We seem to be lacking explicit support for submodules in there -- and those locations should then point
+ // to /yang-library-submodule/{moduleName}(/{moduleRevision})?/{name}(/{revision})? so as to look the
+ // submodule up efficiently and allow for the weird case where there are two submodules with the same name
+ // (that is currently not supported by the parser, but it will be in the future)
+ // - It does not make sense to support yang-ext:mount, unless we also intercept mount points and rewrite
+ // yang-library locations. We most likely want to do that to ensure users are not tempted to connect to
+ // wild destinations
+
+ /**
+ * Get schema of specific module.
+ *
+ * @param identifier path parameter
+ * @param ar {@link AsyncResponse} which needs to be completed
+ */
+ @GET
+ @Produces(YangConstants.RFC6020_YANG_MEDIA_TYPE)
+ @Path("/modules/{identifier:.+}")
+ public void modulesYangGET(@PathParam("identifier") final String identifier, @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYangGET(identifier), ar);
+ }
+
+ /**
+ * Get schema of specific module.
+ *
+ * @param identifier path parameter
+ * @param ar {@link AsyncResponse} which needs to be completed
+ */
+ @GET
+ @Produces(YangConstants.RFC6020_YIN_MEDIA_TYPE)
+ @Path("/modules/{identifier:.+}")
+ public void modulesYinGET(@PathParam("identifier") final String identifier, @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYinGET(identifier), ar);
+ }
+
+ private static void completeModulesGET(final RestconfFuture<ModulesGetResult> future, final AsyncResponse ar) {
+ future.addCallback(new JaxRsRestconfCallback<>(ar) {
+ @Override
+ Response transform(final ModulesGetResult result) {
+ final Reader reader;
+ try {
+ reader = result.source().openStream();
+ } catch (IOException e) {
+ throw new RestconfDocumentedException("Cannot open source", e);
+ }
+ return Response.ok(reader).build();
+ }
+ });
+ }
}
*
* @param <V> value type
*/
-// FIXME: hide this class
-public abstract class JaxRsRestconfCallback<V> extends RestconfCallback<V> {
+abstract class JaxRsRestconfCallback<V> extends RestconfCallback<V> {
private final AsyncResponse ar;
- // FIXME: hide this constructor
- protected JaxRsRestconfCallback(final AsyncResponse ar) {
+ JaxRsRestconfCallback(final AsyncResponse ar) {
this.ar = requireNonNull(ar);
}
@Override
public final void onSuccess(final V result) {
- ar.resume(transform(result));
+ final Response response;
+ try {
+ response = transform(result);
+ } catch (RestconfDocumentedException e) {
+ onFailure(e);
+ return;
+ }
+ ar.resume(response);
}
@Override
ar.resume(failure);
}
- // FIXME: hide this method and its implementations
- protected abstract Response transform(V result);
+ abstract Response transform(V result) throws RestconfDocumentedException;
}
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.JsonPatchStatusBodyWriter;
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.XmlNormalizedNodeBodyWriter;
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.XmlPatchStatusBodyWriter;
-import org.opendaylight.restconf.nb.rfc8040.jersey.providers.YangSchemaExportBodyWriter;
-import org.opendaylight.restconf.nb.rfc8040.jersey.providers.YinSchemaExportBodyWriter;
import org.opendaylight.restconf.nb.rfc8040.jersey.providers.errors.RestconfDocumentedExceptionMapper;
-import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfSchemaServiceImpl;
import org.opendaylight.restconf.server.api.RestconfServer;
final class RestconfApplication extends Application {
final DOMMountPointService mountPointService, final DOMSchemaService domSchemaService) {
singletons = Set.of(
new RestconfDocumentedExceptionMapper(databindProvider),
- new JaxRsRestconf(server),
- new RestconfSchemaServiceImpl(domSchemaService, mountPointService));
+ new JaxRsRestconf(server));
}
@Override
public Set<Class<?>> getClasses() {
return Set.of(
JsonNormalizedNodeBodyWriter.class, XmlNormalizedNodeBodyWriter.class,
- YinSchemaExportBodyWriter.class, YangSchemaExportBodyWriter.class,
JsonPatchStatusBodyWriter.class, XmlPatchStatusBodyWriter.class);
}
import static java.util.Objects.requireNonNull;
+import com.google.common.io.CharSource;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfFuture;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
import org.opendaylight.yangtools.yang.data.codec.xml.XmlCodecFactory;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
/**
* An immutable context holding a consistent view of things related to data bind operations.
*/
public final class DatabindContext {
+ /**
+ * Interface for acquiring model source.
+ */
+ @NonNullByDefault
+ @FunctionalInterface
+ public interface SourceResolver {
+ /**
+ * Resolve a specified source into a byte stream in specified representation.
+ *
+ * @param source Source identifier
+ * @param representation Requested representation
+ * @return A {@link RestconfFuture} completing with an {@link CharSource}, or {@code null} if the requested
+ * representation is not supported.
+ */
+ @Nullable RestconfFuture<CharSource> resolveSource(SourceIdentifier source,
+ Class<? extends SchemaSourceRepresentation> representation);
+ }
+
private static final VarHandle JSON_CODECS;
private static final VarHandle XML_CODECS;
}
private final @NonNull MountPointContext mountContext;
+ private final SourceResolver sourceResolver;
@SuppressWarnings("unused")
private volatile JSONCodecFactory jsonCodecs;
@SuppressWarnings("unused")
private volatile XmlCodecFactory xmlCodecs;
- private DatabindContext(final @NonNull MountPointContext mountContext) {
+ private DatabindContext(final @NonNull MountPointContext mountContext,
+ final @Nullable SourceResolver sourceResolver) {
this.mountContext = requireNonNull(mountContext);
+ this.sourceResolver = sourceResolver;
}
public static @NonNull DatabindContext ofModel(final EffectiveModelContext modelContext) {
- return ofMountPoint(MountPointContext.of(modelContext));
+ return ofModel(modelContext, null);
+ }
+
+ public static @NonNull DatabindContext ofModel(final EffectiveModelContext modelContext,
+ final @Nullable SourceResolver sourceResolver) {
+ return ofMountPoint(MountPointContext.of(modelContext), sourceResolver);
}
public static @NonNull DatabindContext ofMountPoint(final MountPointContext mountContext) {
- return new DatabindContext(mountContext);
+ return ofMountPoint(mountContext, null);
+ }
+
+ public static @NonNull DatabindContext ofMountPoint(final MountPointContext mountContext,
+ final @Nullable SourceResolver sourceResolver) {
+ return new DatabindContext(mountContext, sourceResolver);
}
public @NonNull EffectiveModelContext modelContext() {
final var witness = (XmlCodecFactory) XML_CODECS.compareAndExchangeRelease(this, null, created);
return witness != null ? witness : created;
}
+
+ public @NonNull RestconfFuture<CharSource> resolveSource(final SourceIdentifier source,
+ final Class<? extends SchemaSourceRepresentation> representation) {
+ final var src = requireNonNull(source);
+ if (sourceResolver != null) {
+ final var delegate = sourceResolver.resolveSource(src, representation);
+ if (delegate != null) {
+ return delegate;
+ }
+ }
+ if (YangTextSchemaSource.class.isAssignableFrom(representation)) {
+ return exportSource(mountContext.getEffectiveModelContext(), source, YangCharSource::new,
+ YangCharSource::new);
+ }
+ if (YinTextSchemaSource.class.isAssignableFrom(representation)) {
+ return exportSource(mountContext.getEffectiveModelContext(), source, YinCharSource.OfModule::new,
+ YinCharSource.OfSubmodule::new);
+ }
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Unsupported source representation " + representation.getName()));
+ }
+
+ private static @NonNull RestconfFuture<CharSource> exportSource(final EffectiveModelContext modelContext,
+ final SourceIdentifier source, final Function<ModuleEffectiveStatement, CharSource> moduleCtor,
+ final BiFunction<ModuleEffectiveStatement, SubmoduleEffectiveStatement, CharSource> submoduleCtor) {
+ // If the source identifies a module, things are easy
+ final var name = source.name().getLocalName();
+ final var optRevision = Optional.ofNullable(source.revision());
+ final var optModule = modelContext.findModule(name, optRevision);
+ if (optModule.isPresent()) {
+ return RestconfFuture.of(moduleCtor.apply(optModule.orElseThrow().asEffectiveStatement()));
+ }
+
+ // The source could be a submodule, which we need to hunt down
+ for (var module : modelContext.getModules()) {
+ for (var submodule : module.getSubmodules()) {
+ if (name.equals(submodule.getName()) && optRevision.equals(submodule.getRevision())) {
+ return RestconfFuture.of(submoduleCtor.apply(module.asEffectiveStatement(),
+ submodule.asEffectiveStatement()));
+ }
+ }
+ }
+
+ final var sb = new StringBuilder().append("Source ").append(source.name().getLocalName());
+ optRevision.ifPresent(rev -> sb.append('@').append(rev));
+ sb.append(" not found");
+ return RestconfFuture.failed(new RestconfDocumentedException(sb.toString(),
+ ErrorType.APPLICATION, ErrorTag.DATA_MISSING));
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.CharSource;
+import java.io.Reader;
+import java.io.StringReader;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.export.DeclaredStatementFormatter;
+import org.opendaylight.yangtools.yang.model.export.YangTextSnippet;
+
+final class YangCharSource extends CharSource {
+ private static final DeclaredStatementFormatter FORMATTER = DeclaredStatementFormatter.builder()
+ .retainDefaultStatements()
+ .build();
+
+ private final YangTextSnippet snippet;
+
+ private YangCharSource(final YangTextSnippet snippet) {
+ this.snippet = requireNonNull(snippet);
+ }
+
+ YangCharSource(final ModuleEffectiveStatement module) {
+ this(FORMATTER.toYangTextSnippet(module, module.getDeclared()));
+ }
+
+ YangCharSource(final ModuleEffectiveStatement module, final SubmoduleEffectiveStatement submodule) {
+ this(FORMATTER.toYangTextSnippet(submodule, submodule.getDeclared()));
+ }
+
+ @Override
+ public Reader openStream() {
+ // FIXME: improve this by implementing a Reader which funnels from Iterator<String>
+ return new StringReader(snippet.toString());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.CharSource;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import javax.xml.stream.XMLStreamException;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
+
+abstract sealed class YinCharSource extends CharSource {
+ static final class OfModule extends YinCharSource {
+ private final ModuleEffectiveStatement module;
+
+ OfModule(final ModuleEffectiveStatement module) {
+ this.module = requireNonNull(module);
+ }
+
+ @Override
+ void writeTo(final OutputStream out) throws XMLStreamException {
+ YinExportUtils.writeModuleAsYinText(module, out);
+ }
+ }
+
+ static final class OfSubmodule extends YinCharSource {
+ private final ModuleEffectiveStatement module;
+ private final SubmoduleEffectiveStatement submodule;
+
+ OfSubmodule(final ModuleEffectiveStatement module, final SubmoduleEffectiveStatement submodule) {
+ this.module = requireNonNull(module);
+ this.submodule = requireNonNull(submodule);
+ }
+
+ @Override
+ void writeTo(final OutputStream out) throws XMLStreamException {
+ YinExportUtils.writeSubmoduleAsYinText(module, submodule, out);
+ }
+ }
+
+ @Override
+ public final Reader openStream() throws IOException {
+ final var bos = new ByteArrayOutputStream();
+ try {
+ writeTo(bos);
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to export source", e);
+ }
+ return new StringReader(new String(bos.toByteArray(), StandardCharsets.UTF_8));
+ }
+
+ abstract void writeTo(OutputStream out) throws XMLStreamException;
+}
+++ /dev/null
-/*
- * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.ext.MessageBodyWriter;
-import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
-
-abstract class AbstractSchemaExportBodyWriter implements MessageBodyWriter<SchemaExportContext> {
- @Override
- public final boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
- final MediaType mediaType) {
- return type.equals(SchemaExportContext.class);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.ExecutionException;
-import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.ext.Provider;
-import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
-import org.opendaylight.yangtools.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-
-@Provider
-@Produces(YangConstants.RFC6020_YANG_MEDIA_TYPE)
-public class YangSchemaExportBodyWriter extends AbstractSchemaExportBodyWriter {
- @Override
- public void writeTo(final SchemaExportContext context, final Class<?> type, final Type genericType,
- final Annotation[] annotations, final MediaType mediaType,
- final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException {
- final var module = context.module();
- final var sourceId = new SourceIdentifier(module.argument(),
- module.localQNameModule().getRevision().orElse(null));
- final YangTextSchemaSource yangTextSchemaSource;
- try {
- yangTextSchemaSource = context.sourceProvider().getSource(sourceId).get();
- } catch (InterruptedException | ExecutionException e) {
- throw new WebApplicationException("Unable to retrieve source from SourceProvider.", e);
- }
- yangTextSchemaSource.asByteSource(StandardCharsets.UTF_8).copyTo(entityStream);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.ext.Provider;
-import javax.xml.stream.XMLStreamException;
-import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
-import org.opendaylight.yangtools.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
-
-@Provider
-@Produces(YangConstants.RFC6020_YIN_MEDIA_TYPE)
-public class YinSchemaExportBodyWriter extends AbstractSchemaExportBodyWriter {
- @Override
- public void writeTo(final SchemaExportContext context, final Class<?> type, final Type genericType,
- final Annotation[] annotations, final MediaType mediaType,
- final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException {
- try {
- YinExportUtils.writeModuleAsYinText(context.module(), entityStream);
- } catch (final XMLStreamException e) {
- throw new IOException("Failed to export module", e);
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.legacy;
-
-import static java.util.Objects.requireNonNull;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
-
-/**
- * Holder of schema export context.
- */
-@NonNullByDefault
-public record SchemaExportContext(
- EffectiveModelContext schemaContext,
- ModuleEffectiveStatement module,
- SchemaSourceProvider<YangTextSchemaSource> sourceProvider) {
-
- public SchemaExportContext {
- requireNonNull(schemaContext);
- requireNonNull(module);
- requireNonNull(sourceProvider);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-import java.time.format.DateTimeParseException;
-import java.util.Date;
-import java.util.Locale;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.container.AsyncResponse;
-import javax.ws.rs.container.Suspended;
-import javax.ws.rs.core.Response;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfFuture;
-import org.opendaylight.restconf.nb.jaxrs.JaxRsRestconfCallback;
-import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
-import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
-import org.opendaylight.yangtools.yang.common.ErrorTag;
-import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.common.Revision;
-import org.opendaylight.yangtools.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.common.YangNames;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.Module;
-
-/**
- * Retrieval of the YANG modules which server supports.
- */
-@Path("/")
-public class RestconfSchemaServiceImpl {
- // FIXME: Remove this constant. All logic relying on this constant should instead rely on YangInstanceIdentifier
- // equivalent coming out of argument parsing. This may require keeping List<YangInstanceIdentifier> as the
- // nested path split on yang-ext:mount. This splitting needs to be based on consulting the
- // EffectiveModelContext and allowing it only where yang-ext:mount is actually used in models.
- private static final String MOUNT = "yang-ext:mount";
- private static final Splitter SLASH_SPLITTER = Splitter.on('/');
-
- private final DOMSchemaService schemaService;
- private final DOMMountPointService mountPointService;
- private final DOMYangTextSourceProvider sourceProvider;
-
- /**
- * Default constructor.
- *
- * @param schemaService a {@link DOMSchemaService}
- * @param mountPointService a {@link DOMMountPointService}
- */
- public RestconfSchemaServiceImpl(final DOMSchemaService schemaService,
- final DOMMountPointService mountPointService) {
- this.schemaService = requireNonNull(schemaService);
- this.mountPointService = requireNonNull(mountPointService);
- sourceProvider = schemaService.getExtensions().getInstance(DOMYangTextSourceProvider.class);
- checkArgument(sourceProvider != null, "No DOMYangTextSourceProvider available in %s", schemaService);
- }
-
- /**
- * Get schema of specific module.
- *
- * @param identifier path parameter
- * @param ar {@link AsyncResponse} which needs to be completed with an {@link SchemaExportContext}
- */
- @GET
- @Produces({ YangConstants.RFC6020_YIN_MEDIA_TYPE, YangConstants.RFC6020_YANG_MEDIA_TYPE })
- @Path("modules/{identifier:.+}")
- public void getSchema(@PathParam("identifier") final String identifier, @Suspended final AsyncResponse ar) {
- toSchemaExportContextFromIdentifier(schemaService.getGlobalContext(), identifier, mountPointService,
- sourceProvider).addCallback(new JaxRsRestconfCallback<>(ar) {
- @Override
- protected Response transform(final SchemaExportContext result) {
- return Response.ok(result).build();
- }
- });
- }
-
- /**
- * Parsing {@link Module} module by {@link String} module name and
- * {@link Date} revision and from the parsed module create
- * {@link SchemaExportContext}.
- *
- * @param schemaContext
- * {@link EffectiveModelContext}
- * @param identifier
- * path parameter
- * @param domMountPointService
- * {@link DOMMountPointService}
- * @return {@link SchemaExportContext}
- */
- @VisibleForTesting
- static @NonNull RestconfFuture<SchemaExportContext> toSchemaExportContextFromIdentifier(
- final EffectiveModelContext schemaContext, final String identifier,
- final DOMMountPointService domMountPointService, final DOMYangTextSourceProvider sourceProvider) {
- final var pathComponents = SLASH_SPLITTER.split(identifier);
-
- final var it = pathComponents.iterator();
- final EffectiveModelContext context;
- final Object debugName;
- if (Iterables.contains(pathComponents, MOUNT)) {
- final var sb = new StringBuilder();
- while (true) {
- final var current = it.next();
- sb.append(current);
- if (MOUNT.equals(current) || !it.hasNext()) {
- break;
- }
-
- sb.append('/');
- }
-
- final InstanceIdentifierContext point;
- try {
- point = ParserIdentifier.toInstanceIdentifier(sb.toString(), schemaContext,
- requireNonNull(domMountPointService));
- } catch (RestconfDocumentedException e) {
- return RestconfFuture.failed(e);
- }
-
- final var mountPoint = point.getMountPoint();
- debugName = mountPoint.getIdentifier();
- context = mountPoint.getService(DOMSchemaService.class)
- .map(DOMSchemaService::getGlobalContext)
- .orElse(null);
- if (context == null) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Mount point '" + debugName + "' does not have a model context"));
- }
- } else {
- context = requireNonNull(schemaContext);
- debugName = "controller";
- }
-
- // module name has to be an identifier
- if (!it.hasNext()) {
- return RestconfFuture.failed(new RestconfDocumentedException("Module name must be supplied",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
- }
- final var moduleName = it.next();
- if (moduleName.isEmpty() || !YangNames.IDENTIFIER_START.matches(moduleName.charAt(0))) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Identifier must start with character from set 'a-zA-Z_", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
- }
- if (moduleName.toUpperCase(Locale.ROOT).startsWith("XML")) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Identifier must NOT start with XML ignore case", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
- }
- if (YangNames.NOT_IDENTIFIER_PART.matchesAnyOf(moduleName.substring(1))) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Supplied name has not expected identifier format", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
- }
-
- // YANG Revision-compliant string is required
- if (!it.hasNext()) {
- return RestconfFuture.failed(new RestconfDocumentedException("Revision date must be supplied.",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
- }
- final Revision revision;
- try {
- revision = Revision.of(it.next());
- } catch (final DateTimeParseException e) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Supplied revision is not in expected date format YYYY-mm-dd", e));
- }
-
- final var optModule = context.findModule(moduleName, revision);
- if (optModule.isEmpty()) {
- return RestconfFuture.failed(new RestconfDocumentedException(
- "Module %s %s cannot be found on %s.".formatted(moduleName, revision, debugName),
- ErrorType.APPLICATION, ErrorTag.DATA_MISSING));
- }
-
- return RestconfFuture.of(new SchemaExportContext(context, optModule.orElseThrow().asEffectiveStatement(),
- // FIXME: this does not seem right -- mounts should have their own thing
- sourceProvider));
- }
-}
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.netconf.yanglib.writer.YangLibrarySchemaSourceUrlProvider;
+import org.opendaylight.restconf.nb.jaxrs.JaxRsRestconf;
import org.opendaylight.restconf.nb.rfc8040.URLConstants;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
import org.opendaylight.yangtools.yang.common.Revision;
import org.osgi.service.component.annotations.Component;
/**
- * Component composing schema source URL value on per yang resource basis.
+ * Component composing schema source URL value on per YANG resource basis.
*
* <p>The URL is expected to be requested by {@link org.opendaylight.netconf.yanglib.writer.YangLibraryWriter
* YangLibraryWriter} when yang-library data is being constructed, only default module-set name ("ODL_modules")
* is supported. The composed URL for resource download expected to be served by
- * {@link RestconfSchemaServiceImpl}.
+ * {@link JaxRsRestconf#modulesYangGET(String, javax.ws.rs.container.AsyncResponse)} et al.
*/
@Singleton
@Component(immediate = true, service = YangLibrarySchemaSourceUrlProvider.class)
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.server.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.CharSource;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Result of an {@link RestconfServer#modulesYangGET(String)} invocation.
+ *
+ * @param source A {@link CharSource} containing the body
+ */
+@NonNullByDefault
+public record ModulesGetResult(CharSource source) {
+ public ModulesGetResult {
+ requireNonNull(source);
+ }
+}
// FIXME: this is a simple encoding-variadic return, similar to how OperationsContent is handled use a common
// construct for both cases -- in this case it carries a yang.common.Revision
RestconfFuture<NormalizedNodePayload> yangLibraryVersionGET();
+
+ RestconfFuture<ModulesGetResult> modulesYangGET(String identifier);
+
+ RestconfFuture<ModulesGetResult> modulesYinGET(String identifier);
}
import javax.inject.Inject;
import javax.inject.Singleton;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
import org.opendaylight.yangtools.concepts.Registration;
@Singleton
@Component(service = DatabindProvider.class)
public final class DOMDatabindProvider implements DatabindProvider, EffectiveModelContextListener, AutoCloseable {
+ private final DOMSourceResolver sourceProvider;
private final Registration reg;
private volatile DatabindContext currentContext;
@Inject
@Activate
public DOMDatabindProvider(@Reference final DOMSchemaService schemaService) {
+ final var ext = schemaService.getExtensions().getInstance(DOMYangTextSourceProvider.class);
+ sourceProvider = ext != null ? new DOMSourceResolver(ext) : null;
currentContext = DatabindContext.ofModel(schemaService.getGlobalContext());
reg = schemaService.registerSchemaContextListener(this);
}
public void onModelContextUpdated(final EffectiveModelContext newModelContext) {
final var local = currentContext;
if (local != null && local.modelContext() != newModelContext) {
- currentContext = DatabindContext.ofModel(newModelContext);
+ currentContext = DatabindContext.ofModel(newModelContext, sourceProvider);
}
}
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.server.mdsal;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.io.CharSource;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfFuture;
+import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext.SourceResolver;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+@VisibleForTesting
+public record DOMSourceResolver(@NonNull DOMYangTextSourceProvider domProvider) implements SourceResolver {
+ public DOMSourceResolver {
+ requireNonNull(domProvider);
+ }
+
+ @Override
+ public RestconfFuture<CharSource> resolveSource(final SourceIdentifier source,
+ final Class<? extends SchemaSourceRepresentation> representation) {
+ if (!YangTextSchemaSource.class.isAssignableFrom(representation)) {
+ return null;
+ }
+
+ final var ret = new SettableRestconfFuture<CharSource>();
+ Futures.addCallback(domProvider.getSource(source), new FutureCallback<YangTextSchemaSource>() {
+ @Override
+ public void onSuccess(final YangTextSchemaSource result) {
+ ret.set(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable cause) {
+ ret.setFailure(cause instanceof RestconfDocumentedException e ? e
+ : new RestconfDocumentedException(cause.getMessage(), ErrorType.RPC,
+ ErrorTag.OPERATION_FAILED, cause));
+ }
+ }, MoreExecutors.directExecutor());
+ return ret;
+ }
+}
\ No newline at end of file
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.URI;
+import java.time.format.DateTimeParseException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.opendaylight.mdsal.dom.api.DOMMountPoint;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
import org.opendaylight.restconf.server.api.DataPostResult;
import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
import org.opendaylight.restconf.server.api.DataPutResult;
+import org.opendaylight.restconf.server.api.ModulesGetResult;
import org.opendaylight.restconf.server.api.OperationsGetResult;
import org.opendaylight.restconf.server.api.RestconfServer;
import org.opendaylight.restconf.server.spi.OperationInput;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.common.YangNames;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
}
}
+ // FIXME: Remove this constant. All logic relying on this constant should instead rely on YangInstanceIdentifier
+ // equivalent coming out of argument parsing. This may require keeping List<YangInstanceIdentifier> as the
+ // nested path split on yang-ext:mount. This splitting needs to be based on consulting the
+ // EffectiveModelContext and allowing it only where yang-ext:mount is actually used in models.
+ @Deprecated(forRemoval = true)
+ private static final String MOUNT = "yang-ext:mount";
+ @Deprecated(forRemoval = true)
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+
private final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
private final @NonNull DOMMountPointService mountPointService;
private final @NonNull DatabindProvider databindProvider;
return req.strategy().putData(req.path(), req.data(), insert);
}
+ @Override
+ public RestconfFuture<ModulesGetResult> modulesYangGET(final String identifier) {
+ return modulesGET(identifier, YangTextSchemaSource.class);
+ }
+
+ @Override
+ public RestconfFuture<ModulesGetResult> modulesYinGET(final String identifier) {
+ return modulesGET(identifier, YinTextSchemaSource.class);
+ }
+
+ private @NonNull RestconfFuture<ModulesGetResult> modulesGET(final String identifier,
+ final Class<? extends SchemaSourceRepresentation> representation) {
+ final var currentContext = databindProvider.currentContext();
+ final var pathComponents = SLASH_SPLITTER.split(identifier);
+ final var it = pathComponents.iterator();
+ final DatabindContext databind;
+ final Object debugName;
+ if (Iterables.contains(pathComponents, MOUNT)) {
+ final var sb = new StringBuilder();
+ while (true) {
+ final var current = it.next();
+ sb.append(current);
+ if (MOUNT.equals(current) || !it.hasNext()) {
+ break;
+ }
+
+ sb.append('/');
+ }
+
+ final InstanceIdentifierContext point;
+ try {
+ point = ParserIdentifier.toInstanceIdentifier(sb.toString(), currentContext.modelContext(),
+ mountPointService);
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
+ }
+
+ final var mountPoint = point.getMountPoint();
+ debugName = mountPoint.getIdentifier();
+ final var optSchemaService = mountPoint.getService(DOMSchemaService.class);
+ if (optSchemaService.isEmpty()) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Mount point '" + debugName + "' does not expose models"));
+ }
+ final var schemaService = optSchemaService.orElseThrow();
+ final var modelContext = schemaService.getGlobalContext();
+ if (modelContext == null) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Mount point '" + debugName + "' does not have a model context"));
+ }
+ final var domProvider = schemaService.getExtensions().getInstance(DOMYangTextSourceProvider.class);
+ databind = DatabindContext.ofModel(modelContext,
+ domProvider == null ? null : new DOMSourceResolver(domProvider));
+ } else {
+ databind = currentContext;
+ debugName = "controller";
+ }
+
+ // module name has to be an identifier
+ if (!it.hasNext()) {
+ return RestconfFuture.failed(new RestconfDocumentedException("Module name must be supplied",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
+ }
+ final var moduleName = it.next();
+ if (moduleName.isEmpty() || !YangNames.IDENTIFIER_START.matches(moduleName.charAt(0))) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Identifier must start with character from set 'a-zA-Z_", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
+ }
+ if (moduleName.toUpperCase(Locale.ROOT).startsWith("XML")) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Identifier must NOT start with XML ignore case", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
+ }
+ if (YangNames.NOT_IDENTIFIER_PART.matchesAnyOf(moduleName.substring(1))) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Supplied name has not expected identifier format", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
+ }
+
+ // YANG Revision-compliant string is required
+ if (!it.hasNext()) {
+ return RestconfFuture.failed(new RestconfDocumentedException("Revision date must be supplied.",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
+ }
+ final Revision revision;
+ try {
+ revision = Revision.of(it.next());
+ } catch (final DateTimeParseException e) {
+ return RestconfFuture.failed(new RestconfDocumentedException(
+ "Supplied revision is not in expected date format YYYY-mm-dd", e));
+ }
+
+ return databind.resolveSource(new SourceIdentifier(moduleName, revision), representation)
+ .transform(ModulesGetResult::new);
+ }
+
@Override
public RestconfFuture<OperationsGetResult> operationsGET() {
return operationsGET(databindProvider.currentContext().modelContext());
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
+package org.opendaylight.restconf.nb.jaxrs;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doReturn;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.Reader;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.function.Executable;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-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.api.DOMYangTextSourceProvider;
import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfError;
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.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
@ExtendWith(MockitoExtension.class)
-class RestconfModulesGetTest {
+class RestconfModulesGetTest extends AbstractRestconfTest {
private static final EffectiveModelContext MODEL_CONTEXT =
YangParserTestUtils.parseYangResourceDirectory("/parser-identifier");
private static final EffectiveModelContext MODEL_CONTEXT_ON_MOUNT_POINT =
@Mock
private DOMYangTextSourceProvider sourceProvider;
- @Mock
- private DOMMountPoint mountPoint;
- @Mock
- private DOMMountPointService mountPointService;
+
+ @Override
+ EffectiveModelContext modelContext() {
+ return MODEL_CONTEXT;
+ }
/**
* Positive test of getting <code>SchemaExportContext</code>. Expected module name, revision and namespace are
*/
@Test
void toSchemaExportContextFromIdentifierTest() {
- final var exportContext = RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, null, sourceProvider).getOrThrow();
- final var module = exportContext.module();
- assertNotNull(module);
- assertEquals(TEST_MODULE_NAME, module.argument().getLocalName());
- final var namespace = module.localQNameModule();
- assertEquals(Revision.ofNullable(TEST_MODULE_REVISION), namespace.getRevision());
- assertEquals(TEST_MODULE_NAMESPACE, namespace.getNamespace().toString());
+ assertEquals("""
+ module test-module {
+ namespace test:module;
+ prefix testm;
+ yang-version 1;
+ revision 2016-06-02 {
+ description
+ "Initial revision.";
+ }
+ }
+ """, assertYang(TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION));
}
/**
*/
@Test
void toSchemaExportContextFromIdentifierNotFoundTest() {
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(
- MODEL_CONTEXT, "not-existing-module" + "/" + "2016-01-01", null, sourceProvider)
- .getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
- assertEquals("Module not-existing-module 2016-01-01 cannot be found on controller.", error.getErrorMessage());
+ final var error = assertError(ar -> restconf.modulesYinGET("not-existing-module/2016-01-01", ar));
+ assertEquals("Source not-existing-module@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
}
*/
@Test
void toSchemaExportContextFromIdentifierInvalidIdentifierNegativeTest() {
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, null, sourceProvider).getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> restconf.modulesYangGET(TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
void toSchemaExportContextFromIdentifierMountPointTest() {
mockMountPoint();
- final var exportContext = RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION,
- mountPointService, sourceProvider).getOrThrow();
-
- final var module = exportContext.module();
- assertEquals(TEST_MODULE_NAME, module.argument().getLocalName());
- final var namespace = module.localQNameModule();
- assertEquals(Revision.ofNullable(TEST_MODULE_REVISION), namespace.getRevision());
- assertEquals(TEST_MODULE_NAMESPACE, namespace.getNamespace().toString());
+ final var content = assertYang(MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION);
+ assertEquals("""
+ module test-module {
+ namespace test:module;
+ prefix testm;
+ yang-version 1;
+ revision 2016-06-02 {
+ description
+ "Initial revision.";
+ }
+ }
+ """, content);
}
/**
mockMountPoint();
doReturn(MOUNT_IID).when(mountPoint).getIdentifier();
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- MOUNT_POINT_IDENT + "/" + "not-existing-module" + "/" + "2016-01-01",
- mountPointService, sourceProvider)
- .getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
- assertEquals("Module not-existing-module 2016-01-01 cannot be found on "
- + "/(mount:point?revision=2016-06-02)mount-container/point-number.", error.getErrorMessage());
+ final var error = assertError(
+ ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT + "/" + "not-existing-module" + "/" + "2016-01-01", ar));
+ assertEquals("Source not-existing-module@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
}
void toSchemaExportContextFromIdentifierMountPointInvalidIdentifierNegativeTest() {
mockMountPoint();
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- MOUNT_POINT_IDENT + "/" + TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, mountPointService,
- sourceProvider).getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(
+ ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT + "/" + TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
}
- /**
- * Negative test of getting <code>SchemaExportContext</code> when supplied identifier is null.
- * <code>NullPointerException</code> is expected. <code>DOMMountPointService</code> is not used.
- */
- @Test
- void toSchemaExportContextFromIdentifierNullIdentifierNegativeTest() {
- assertThrows(NullPointerException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, null, null,
- sourceProvider));
- }
-
- /**
- * Negative test of of getting <code>SchemaExportContext</code> when supplied <code>SchemaContext</code> is
- * <code>null</code>. Test is expected to fail with <code>NullPointerException</code>.
- */
- @Test
- void toSchemaExportContextFromIdentifierNullSchemaContextNegativeTest() {
- assertThrows(NullPointerException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(null,
- TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, null, sourceProvider));
- }
-
- /**
- * Negative test of of getting <code>SchemaExportContext</code> when supplied <code>SchemaContext</code> is
- * <code>null</code> and identifier specifies module behind mount point. Test is expected to fail with
- * <code>NullPointerException</code>.
- */
- @Test
- void toSchemaExportContextFromIdentifierMountPointNullSchemaContextNegativeTest() {
- assertThrows(NullPointerException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(null,
- MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, mountPointService,
- sourceProvider));
- }
-
- /**
- * Negative test of of getting <code>SchemaExportContext</code> when supplied <code>DOMMountPointService</code>
- * is <code>null</code> and identifier defines module behind mount point. Test is expected to fail with
- * <code>NullPointerException</code>.
- */
- @Test
- void toSchemaExportContextFromIdentifierNullMountPointServiceNegativeTest() {
- assertThrows(NullPointerException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(
- MODEL_CONTEXT, MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, null,
- sourceProvider));
- }
-
@Test
void toSchemaExportContextFromIdentifierNullSchemaContextBehindMountPointNegativeTest() {
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- "/yang-ext:mount/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, mountPointService,
- sourceProvider).getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(
+ ar -> restconf.modulesYangGET("/yang-ext:mount/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, ar));
// FIXME: this should be something different
assertEquals("Identifier may not be empty", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
*/
@Test
void validateAndGetRevisionNotSuppliedTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, "module", null, null)
- .getOrThrow());
+ final var error = assertInvalidValue("module");
assertEquals("Revision date must be supplied.", error.getErrorMessage());
}
*/
@Test
void validateAndGetRevisionNotParsableTest() {
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- "module/not-parsable-as-date", null, null).getOrThrow());
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> restconf.modulesYangGET("module/not-parsable-as-date", ar));
assertEquals("Supplied revision is not in expected date format YYYY-mm-dd", error.getErrorMessage());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
void validateAndGetModulNameNotSuppliedTest() {
mockMountPoint();
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, MOUNT_POINT_IDENT,
- mountPointService, null).getOrThrow());
+ final var error = assertInvalidValue(MOUNT_POINT_IDENT);
assertEquals("Module name must be supplied", error.getErrorMessage());
}
*/
@Test
void validateAndGetModuleNameNotParsableFirstTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- "01-not-parsable-as-name-on-first-char", null, null).getOrThrow());
+ final var error = assertInvalidValue("01-not-parsable-as-name-on-first-char");
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
}
*/
@Test
public void validateAndGetModuleNameNotParsableNextTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- "not-parsable-as-name-after-first-char*", null, null).getOrThrow());
+ final var error = assertInvalidValue("not-parsable-as-name-after-first-char*");
assertEquals("Supplied name has not expected identifier format", error.getErrorMessage());
}
*/
@Test
void validateAndGetModuleNameEmptyTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, "", null, null)
- .getOrThrow());
+ final var error = assertInvalidValue("");
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
}
- private static RestconfError assertInvalidValue(final Executable runnable) {
- final var ex = assertThrows(RestconfDocumentedException.class, runnable);
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ private String assertYang(final String identifier) {
+ try (var reader = assertEntity(Reader.class, 200, ar -> restconf.modulesYangGET(identifier, ar))) {
+ return CharStreams.toString(reader);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private RestconfError assertInvalidValue(final String identifier) {
+ final var error = assertError(ar -> restconf.modulesYangGET(identifier, ar));
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
return error;
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
+package org.opendaylight.restconf.nb.jaxrs;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.doReturn;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertEntity;
import static org.opendaylight.restconf.nb.jaxrs.AbstractRestconfTest.assertError;
-import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.io.CharStreams;
+import com.google.common.util.concurrent.Futures;
+import java.io.Reader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
import org.opendaylight.mdsal.dom.broker.DOMMountPointServiceImpl;
import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
-import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
+import org.opendaylight.restconf.server.mdsal.DOMSourceResolver;
+import org.opendaylight.restconf.server.mdsal.MdsalRestconfServer;
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.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
/**
private static final EffectiveModelContext SCHEMA_CONTEXT_WITH_MOUNT_POINTS =
YangParserTestUtils.parseYangResourceDirectory("/modules/mount-points");
- // service under test
- private RestconfSchemaServiceImpl schemaService;
-
// handlers
@Mock
- private DOMSchemaService mockSchemaService;
+ private DOMSchemaService schemaService;
+ @Mock
+ private DOMYangTextSourceProvider sourceProvider;
+ @Mock
+ private DOMDataBroker dataBroker;
+ @Mock
+ private DOMActionService actionService;
+ @Mock
+ private DOMRpcService rpcService;
+ @Mock
+ private YangTextSchemaSource yangSource;
@Mock
- private DOMYangTextSourceProvider mockSourceProvider;
+ private Reader yangReader;
+
+ // service under test
+ private JaxRsRestconf restconf;
@Before
public void setup() throws Exception {
.createMountPoint(YangInstanceIdentifier.of(QName.create("mount:point:2", "2016-01-01", "cont")))
.register();
- doReturn(ImmutableClassToInstanceMap.of(DOMYangTextSourceProvider.class, mockSourceProvider))
- .when(mockSchemaService).getExtensions();
- schemaService = new RestconfSchemaServiceImpl(mockSchemaService, mountPointService);
+ restconf = new JaxRsRestconf(new MdsalRestconfServer(
+ () -> DatabindContext.ofModel(schemaService.getGlobalContext(), new DOMSourceResolver(sourceProvider)),
+ dataBroker, rpcService, actionService, mountPointService));
}
/**
* Get schema with identifier of existing module and check if correct module was found.
*/
@Test
- public void getSchemaTest() {
+ public void getSchemaTest() throws Exception {
// prepare conditions - return not-mount point schema context
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
-
- // make test
- final var exportContext = assertEntity(SchemaExportContext.class, 200,
- ar -> schemaService.getSchema(TEST_MODULE, ar));
-
- // verify
- assertNotNull(exportContext);
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
+ doReturn(Futures.immediateFuture(yangSource)).when(sourceProvider)
+ .getSource(new SourceIdentifier("module1", Revision.of("2014-01-01")));
+ doReturn(yangReader).when(yangSource).openStream();
- final var module = exportContext.module();
- assertEquals("module1", module.argument().getLocalName());
- final var namespace = module.localQNameModule();
- assertEquals(Revision.ofNullable("2014-01-01"), namespace.getRevision());
- assertEquals("module:1", namespace.getNamespace().toString());
+ assertSame(yangReader, assertEntity(Reader.class, 200, ar -> restconf.modulesYangGET(TEST_MODULE, ar)));
}
/**
@Test
public void getSchemaForNotExistingModuleTest() {
// prepare conditions - return not-mount point schema context
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> schemaService.getSchema(NOT_EXISTING_MODULE, ar));
- assertEquals("Module not-existing 2016-01-01 cannot be found on controller.", error.getErrorMessage());
+ final var error = assertError(ar -> restconf.modulesYinGET(NOT_EXISTING_MODULE, ar));
+ assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
}
* Get schema with identifier of existing module behind mount point and check if correct module was found.
*/
@Test
- public void getSchemaMountPointTest() {
+ public void getSchemaMountPointTest() throws Exception {
// prepare conditions - return schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
-
- // make test
- final var exportContext = assertEntity(SchemaExportContext.class, 200,
- ar -> schemaService.getSchema(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
-
- // verify
- assertNotNull(exportContext);
-
- final var module = exportContext.module();
- assertEquals("module1-behind-mount-point", module.argument().getLocalName());
- final var namespace = module.localQNameModule();
- assertEquals(Revision.ofNullable("2014-02-03"), namespace.getRevision());
- assertEquals("module:1:behind:mount:point", namespace.getNamespace().toString());
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
+
+ final var reader = assertEntity(Reader.class, 200,
+ ar -> restconf.modulesYangGET(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
+ assertEquals("""
+ module module1-behind-mount-point {
+ namespace module:1:behind:mount:point;
+ prefix mod1bemopo;
+ revision 2014-02-03;
+ rpc rpc-behind-module1;
+ }
+ """, CharStreams.toString(reader));
}
/**
@Test
public void getSchemaForNotExistingModuleMountPointTest() {
// prepare conditions - return schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> schemaService.getSchema(MOUNT_POINT + NOT_EXISTING_MODULE, ar));
- assertEquals("Module not-existing 2016-01-01 cannot be found on /(mount:point:1?revision=2016-01-01)cont.",
- error.getErrorMessage());
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + NOT_EXISTING_MODULE, ar));
+ assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
}
@Test
public void getSchemaNullSchemaContextBehindMountPointTest() {
// prepare conditions - return correct schema context for mount points (this is not null)
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test - call service on mount point with null schema context
// NULL_MOUNT_POINT contains null schema context
final var error = assertError(
- ar -> schemaService.getSchema(NULL_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
+ ar -> restconf.modulesYangGET(NULL_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
assertEquals("Mount point mount-point-2:cont does not expose DOMSchemaService", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTags.RESOURCE_DENIED_TRANSPORT, error.getErrorTag());
@Test
public void getSchemaWithEmptyIdentifierTest() {
// prepare conditions - return correct schema context
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> schemaService.getSchema("", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWithEmptyIdentifierMountPointTest() {
// prepare conditions - return correct schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema(MOUNT_POINT + "", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWithNotParsableIdentifierTest() {
// prepare conditions - return correct schema context without mount points
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema("01_module/2016-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("01_module/2016-01-01", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWithNotParsableIdentifierMountPointTest() {
// prepare conditions - return correct schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema(MOUNT_POINT + "01_module/2016-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "01_module/2016-01-01", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWrongIdentifierTest() {
// prepare conditions - return correct schema context without mount points
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema("2014-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("2014-01-01", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWrongIdentifierMountPointTest() {
// prepare conditions - return correct schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema(MOUNT_POINT + "2014-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "2014-01-01", ar));
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWithoutRevisionTest() {
// prepare conditions - return correct schema context without mount points
- doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema("module", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("module", ar));
assertEquals("Revision date must be supplied.", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaWithoutRevisionMountPointTest() {
// prepare conditions - return correct schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> schemaService.getSchema(MOUNT_POINT + "module", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "module", ar));
assertEquals("Revision date must be supplied.", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
@Test
public void getSchemaContextWithNotExistingMountPointTest() {
// prepare conditions - return schema context with mount points
- doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
final var error = assertError(
- ar -> schemaService.getSchema(NOT_EXISTING_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
+ ar -> restconf.modulesYangGET(NOT_EXISTING_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
assertEquals("Failed to lookup for module with name 'mount-point-3'.", error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());