import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import org.opendaylight.restconf.common.errors.RestconfFuture;
import org.opendaylight.restconf.common.patch.PatchStatusContext;
import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
+import org.opendaylight.restconf.nb.rfc8040.URLConstants;
import org.opendaylight.restconf.nb.rfc8040.databind.JsonChildBody;
import org.opendaylight.restconf.nb.rfc8040.databind.JsonDataPostBody;
import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
/**
* Get schema of specific module.
*
- * @param identifier path parameter
+ * @param fileName source file name
+ * @param revision source revision
* @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);
+ @Path("/" + URLConstants.MODULES_SUBPATH + "/{fileName : [^/]+}")
+ public void modulesYangGET(@PathParam("fileName") final String fileName,
+ @QueryParam("revision") final String revision, @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYangGET(fileName, revision), ar);
}
/**
* Get schema of specific module.
*
- * @param identifier path parameter
+ * @param mountPath mount point path
+ * @param fileName source file name
+ * @param revision source revision
+ * @param ar {@link AsyncResponse} which needs to be completed
+ */
+ @GET
+ @Produces(YangConstants.RFC6020_YANG_MEDIA_TYPE)
+ @Path("/" + URLConstants.MODULES_SUBPATH + "/{mountPath:.+}/{fileName : [^/]+}")
+ public void modulesYangGET(@Encoded @PathParam("mountPath") final ApiPath mountPath,
+ @PathParam("fileName") final String fileName, @QueryParam("revision") final String revision,
+ @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYangGET(mountPath, fileName, revision), ar);
+ }
+
+ /**
+ * Get schema of specific module.
+ *
+ * @param fileName source file name
+ * @param revision source revision
* @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);
+ @Path("/" + URLConstants.MODULES_SUBPATH + "/{fileName : [^/]+}")
+ public void modulesYinGET(@PathParam("fileName") final String fileName,
+ @QueryParam("revision") final String revision, @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYinGET(fileName, revision), ar);
+ }
+
+ /**
+ * Get schema of specific module.
+ *
+ * @param mountPath mount point path
+ * @param fileName source file name
+ * @param revision source revision
+ * @param ar {@link AsyncResponse} which needs to be completed
+ */
+ @GET
+ @Produces(YangConstants.RFC6020_YIN_MEDIA_TYPE)
+ @Path("/" + URLConstants.MODULES_SUBPATH + "/{mountPath:.+}/{fileName : [^/]+}")
+ public void modulesYinGET(@Encoded @PathParam("mountPath") final ApiPath mountPath,
+ @PathParam("fileName") final String fileName, @QueryParam("revision") final String revision,
+ @Suspended final AsyncResponse ar) {
+ completeModulesGET(server.modulesYinGET(mountPath, fileName, revision), ar);
}
private static void completeModulesGET(final RestconfFuture<ModulesGetResult> future, final AsyncResponse ar) {
*/
public static final String BASE_PATH = "rests";
/**
- * The second URL path element for streams supper, i.e. {@code https://localhost/BASE_PATH/STREAMS}.
+ * The second URL path element for streams support, i.e. {@code https://localhost/BASE_PATH/STREAMS}.
*/
public static final String STREAMS_SUBPATH = "streams";
+ /**
+ * The second URL path element for YANG library module support, i.e. {@code https://localhost/BASE_PATH/MODULES}.
+ */
+ public static final String MODULES_SUBPATH = "modules";
+ /**
+ * The query parameter carrying the optional revision in YANG library module support, i.e.
+ * {@code https://localhost/BASE_PATH/MODULES?REVISION=2023-11-26}.
+ */
+ public static final String MODULES_REVISION_QUERY = "revision";
private URLConstants() {
// Hidden on purpose
ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT));
final var nextModelContext = nextMountPoint.getService(DOMSchemaService.class)
.orElseThrow(() -> new RestconfDocumentedException(
- "Mount point " + userPath + " does not expose DOMSchemaService",
+ "Mount point '" + userPath + "' does not expose DOMSchemaService",
ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT))
.getGlobalContext();
+ if (nextModelContext == null) {
+ throw new RestconfDocumentedException("Mount point '" + userPath + "' does not have any models",
+ ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT);
+ }
prefix = mount + 1;
currentModelContext = nextModelContext;
* <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 JaxRsRestconf#modulesYangGET(String, javax.ws.rs.container.AsyncResponse)} et al.
+ * {@link JaxRsRestconf#modulesYangGET(String, String, javax.ws.rs.container.AsyncResponse)} et al.
*/
@Singleton
@Component(immediate = true, service = YangLibrarySchemaSourceUrlProvider.class)
public Optional<Uri> getSchemaSourceUrl(final @NonNull String moduleSetName,
final @NonNull String moduleName, final @Nullable Revision revision) {
if ("ODL_modules".equals(moduleSetName)) {
- // "/modules/.*" path is mapped to RestconfSchemaService#getSchema(String) using JaxRS annotation
- final var sb = new StringBuilder("/" + URLConstants.BASE_PATH + "/modules/").append(moduleName);
+ final var sb = new StringBuilder("/" + URLConstants.BASE_PATH + "/" + URLConstants.MODULES_SUBPATH + "/")
+ .append(moduleName);
if (revision != null) {
- sb.append("/").append(revision);
+ sb.append("?" + URLConstants.MODULES_REVISION_QUERY + "=").append(revision);
}
return Optional.of(new Uri(sb.toString()));
}
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
- * Result of an {@link RestconfServer#modulesYangGET(String)} invocation.
+ * Result of an {@link RestconfServer#modulesYangGET(String, String)} invocation.
*
* @param source A {@link CharSource} containing the body
*/
import java.net.URI;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfFuture;
import org.opendaylight.restconf.common.patch.PatchStatusContext;
// construct for both cases -- in this case it carries a yang.common.Revision
RestconfFuture<NormalizedNodePayload> yangLibraryVersionGET();
- RestconfFuture<ModulesGetResult> modulesYangGET(String identifier);
+ RestconfFuture<ModulesGetResult> modulesYangGET(String fileName, @Nullable String revision);
- RestconfFuture<ModulesGetResult> modulesYinGET(String identifier);
+ RestconfFuture<ModulesGetResult> modulesYangGET(ApiPath mountPath, String fileName, @Nullable String revision);
+
+ RestconfFuture<ModulesGetResult> modulesYinGET(String fileName, @Nullable String revision);
+
+ RestconfFuture<ModulesGetResult> modulesYinGET(ApiPath mountPath, String fileName, @Nullable String revision);
}
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 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;
}
@Override
- public RestconfFuture<ModulesGetResult> modulesYangGET(final String identifier) {
- return modulesGET(identifier, YangTextSchemaSource.class);
+ public RestconfFuture<ModulesGetResult> modulesYangGET(final String fileName, final String revision) {
+ return modulesGET(fileName, revision, YangTextSchemaSource.class);
}
@Override
- public RestconfFuture<ModulesGetResult> modulesYinGET(final String identifier) {
- return modulesGET(identifier, YinTextSchemaSource.class);
+ public RestconfFuture<ModulesGetResult> modulesYangGET(final ApiPath mountPath, final String fileName,
+ final String revision) {
+ return modulesGET(mountPath, fileName, revision, YangTextSchemaSource.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;
- }
+ @Override
+ public RestconfFuture<ModulesGetResult> modulesYinGET(final String fileName, final String revision) {
+ return modulesGET(fileName, revision, YinTextSchemaSource.class);
+ }
- sb.append('/');
- }
+ @Override
+ public RestconfFuture<ModulesGetResult> modulesYinGET(final ApiPath mountPath, final String fileName,
+ final String revision) {
+ return modulesGET(mountPath, fileName, revision, YinTextSchemaSource.class);
+ }
- final InstanceIdentifierContext point;
- try {
- point = ParserIdentifier.toInstanceIdentifier(sb.toString(), currentContext.modelContext(),
- mountPointService);
- } catch (RestconfDocumentedException e) {
- return RestconfFuture.failed(e);
- }
+ private @NonNull RestconfFuture<ModulesGetResult> modulesGET(final String fileName, final String revision,
+ final Class<? extends SchemaSourceRepresentation> representation) {
+ return modulesGET(databindProvider.currentContext(), fileName, revision, representation);
+ }
- 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";
+ private @NonNull RestconfFuture<ModulesGetResult> modulesGET(final ApiPath mountPath, final String fileName,
+ final String revision, final Class<? extends SchemaSourceRepresentation> representation) {
+ final var mountOffset = mountPath.indexOf("yang-ext", "mount");
+ if (mountOffset != mountPath.steps().size() - 1) {
+ return RestconfFuture.failed(new RestconfDocumentedException("Mount path has to end with yang-ext:mount"));
+ }
+
+ final var currentContext = databindProvider.currentContext();
+ final InstanceIdentifierContext point;
+ try {
+ point = InstanceIdentifierContext.ofApiPath(mountPath, currentContext.modelContext(), mountPointService);
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
}
- // module name has to be an identifier
- if (!it.hasNext()) {
+ final var mountPoint = point.getMountPoint();
+ final var modelContext = point.getSchemaContext();
+ final var domProvider = mountPoint.getService(DOMSchemaService.class)
+ .flatMap(svc -> Optional.ofNullable(svc.getExtensions().getInstance(DOMYangTextSourceProvider.class)));
+
+ return modulesGET(DatabindContext.ofModel(modelContext,
+ domProvider.isEmpty() ? null : new DOMSourceResolver(domProvider.orElseThrow())), fileName, revision,
+ representation);
+ }
+
+ private static @NonNull RestconfFuture<ModulesGetResult> modulesGET(final DatabindContext databind,
+ final String moduleName, final String revisionStr,
+ final Class<? extends SchemaSourceRepresentation> representation) {
+ if (moduleName == null) {
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));
}
// 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());
+ revision = Revision.ofNullable(revisionStr).orElse(null);
} catch (final DateTimeParseException e) {
return RestconfFuture.failed(new RestconfDocumentedException(
- "Supplied revision is not in expected date format YYYY-mm-dd", e));
+ "Supplied revision is not in expected date format YYYY-mm-dd",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e));
}
return databind.resolveSource(new SourceIdentifier(moduleName, revision), representation)
import java.io.IOException;
import java.io.Reader;
import java.util.Optional;
+import java.util.function.Consumer;
+import javax.ws.rs.container.AsyncResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
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.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
private static final String TEST_MODULE_NAME = "test-module";
private static final String TEST_MODULE_REVISION = "2016-06-02";
private static final String TEST_MODULE_NAMESPACE = "test:module";
- private static final String MOUNT_POINT_IDENT = "mount-point:mount-container/point-number/yang-ext:mount";
+ private static final ApiPath MOUNT_POINT_IDENT = apiPath("mount-point:mount-container/point-number/yang-ext:mount");
private static final YangInstanceIdentifier MOUNT_IID = YangInstanceIdentifier.of(
QName.create("mount:point", "2016-06-02", "mount-container"),
QName.create("mount:point", "2016-06-02", "point-number"));
@Mock
private DOMYangTextSourceProvider sourceProvider;
+ @Mock
+ private DOMSchemaService schemaService;
@Override
EffectiveModelContext modelContext() {
"Initial revision.";
}
}
- """, assertYang(TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION));
+ """, assertYang(null, TEST_MODULE_NAME, TEST_MODULE_REVISION));
}
/**
*/
@Test
void toSchemaExportContextFromIdentifierNotFoundTest() {
- final var error = assertError(ar -> restconf.modulesYinGET("not-existing-module/2016-01-01", ar));
+ 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 error = assertError(ar -> restconf.modulesYangGET(TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, ar));
+ 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 content = assertYang(MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION);
+ final var content = assertYang(MOUNT_POINT_IDENT, TEST_MODULE_NAME, TEST_MODULE_REVISION);
assertEquals("""
module test-module {
namespace test:module;
@Test
void toSchemaExportContextFromIdentifierMountPointNotFoundTest() {
mockMountPoint();
- doReturn(MOUNT_IID).when(mountPoint).getIdentifier();
final var error = assertError(
- ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT + "/" + "not-existing-module" + "/" + "2016-01-01", ar));
+ 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());
mockMountPoint();
final var error = assertError(
- ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT + "/" + TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, ar));
+ 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());
@Test
void toSchemaExportContextFromIdentifierNullSchemaContextBehindMountPointNegativeTest() {
+ doReturn(Optional.of(schemaService)).when(mountPoint).getService(DOMSchemaService.class);
+ doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(MOUNT_IID);
+
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());
+ ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT, TEST_MODULE_NAME, TEST_MODULE_REVISION, ar));
+ assertEquals("Mount point 'mount-point:mount-container/point-number' does not have any models",
+ error.getErrorMessage());
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
- assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
+ assertEquals(ErrorTags.RESOURCE_DENIED_TRANSPORT, error.getErrorTag());
}
private void mockMountPoint() {
*/
@Test
void validateAndGetRevisionNotSuppliedTest() {
- final var error = assertInvalidValue("module");
- assertEquals("Revision date must be supplied.", error.getErrorMessage());
+ final var error = assertError(ar -> restconf.modulesYangGET("module", null, ar));
+ assertEquals("Source module not found", error.getErrorMessage());
+ assertEquals(ErrorType.APPLICATION, error.getErrorType());
+ assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
}
/**
*/
@Test
void validateAndGetRevisionNotParsableTest() {
- final var error = assertError(ar -> restconf.modulesYangGET("module/not-parsable-as-date", ar));
+ final var error = assertInvalidValue("module", "not-parsable-as-date");
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());
}
/**
*/
@Test
void validateAndGetModulNameNotSuppliedTest() {
- mockMountPoint();
-
- final var error = assertInvalidValue(MOUNT_POINT_IDENT);
+ final var error = assertInvalidValue(null, null);
assertEquals("Module name must be supplied", error.getErrorMessage());
}
*/
@Test
void validateAndGetModuleNameNotParsableFirstTest() {
- final var error = assertInvalidValue("01-not-parsable-as-name-on-first-char");
+ final var error = assertInvalidValue("01-not-parsable-as-name-on-first-char", null);
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
}
*/
@Test
public void validateAndGetModuleNameNotParsableNextTest() {
- final var error = assertInvalidValue("not-parsable-as-name-after-first-char*");
+ final var error = assertInvalidValue("not-parsable-as-name-after-first-char*", null);
assertEquals("Supplied name has not expected identifier format", error.getErrorMessage());
}
*/
@Test
void validateAndGetModuleNameEmptyTest() {
- final var error = assertInvalidValue("");
+ final var error = assertInvalidValue("", null);
assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
}
- private String assertYang(final String identifier) {
- try (var reader = assertEntity(Reader.class, 200, ar -> restconf.modulesYangGET(identifier, ar))) {
+ private String assertYang(final ApiPath mountPath, final String fileName, final String revision) {
+ final Consumer<AsyncResponse> invocation = mountPath != null
+ ? ar -> restconf.modulesYangGET(mountPath, fileName, revision, ar)
+ : ar -> restconf.modulesYangGET(fileName, revision, ar);
+ try (var reader = assertEntity(Reader.class, 200, invocation)) {
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));
+ private RestconfError assertInvalidValue(final String fileName, final String revision) {
+ final var error = assertError(ar -> restconf.modulesYangGET(fileName, revision, ar));
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
return error;
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.api.ApiPath;
import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
import org.opendaylight.restconf.server.mdsal.DOMSourceResolver;
*/
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class RestconfSchemaServiceTest {
- private static final String MOUNT_POINT = "mount-point-1:cont/yang-ext:mount/";
- private static final String NULL_MOUNT_POINT = "mount-point-2:cont/yang-ext:mount/";
- private static final String NOT_EXISTING_MOUNT_POINT = "mount-point-3:cont/yang-ext:mount/";
-
- private static final String TEST_MODULE = "module1/2014-01-01";
- private static final String TEST_MODULE_BEHIND_MOUNT_POINT = "module1-behind-mount-point/2014-02-03";
- private static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
+ private static final ApiPath MOUNT_POINT = AbstractRestconfTest.apiPath("mount-point-1:cont/yang-ext:mount");
+ private static final ApiPath NULL_MOUNT_POINT = AbstractRestconfTest.apiPath("mount-point-2:cont/yang-ext:mount");
+ private static final ApiPath NOT_EXISTING_MOUNT_POINT =
+ AbstractRestconfTest.apiPath("mount-point-3:cont/yang-ext:mount");
// schema context with modules
private static final EffectiveModelContext SCHEMA_CONTEXT =
.getSource(new SourceIdentifier("module1", Revision.of("2014-01-01")));
doReturn(yangReader).when(yangSource).openStream();
- assertSame(yangReader, assertEntity(Reader.class, 200, ar -> restconf.modulesYangGET(TEST_MODULE, ar)));
+ assertSame(yangReader, assertEntity(Reader.class, 200,
+ ar -> restconf.modulesYangGET("module1", "2014-01-01", ar)));
}
/**
// prepare conditions - return not-mount point schema context
doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> restconf.modulesYinGET(NOT_EXISTING_MODULE, ar));
+ final var error = assertError(ar -> restconf.modulesYinGET("not-existing", "2016-01-01", ar));
assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
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));
+ ar -> restconf.modulesYangGET(MOUNT_POINT, "module1-behind-mount-point", "2014-02-03", ar));
assertEquals("""
module module1-behind-mount-point {
namespace module:1:behind:mount:point;
// prepare conditions - return schema context with mount points
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + NOT_EXISTING_MODULE, ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT, "not-existing", "2016-01-01", ar));
assertEquals("Source not-existing@2016-01-01 not found", error.getErrorMessage());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
// make test - call service on mount point with null schema context
// NULL_MOUNT_POINT contains null schema context
final var error = assertError(
- 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());
+ ar -> restconf.modulesYangGET(NULL_MOUNT_POINT, "module1-behind-mount-point", "2014-02-03", 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());
}
// prepare conditions - return correct schema context
doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
- final var error = assertError(ar -> restconf.modulesYangGET("", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("", null, 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());
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT, "", null, 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());
doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> restconf.modulesYangGET("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());
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> restconf.modulesYangGET(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());
doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> restconf.modulesYangGET("2014-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET("2014-01-01", null, 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());
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT + "2014-01-01", ar));
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT, "2014-01-01", null, 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());
doReturn(SCHEMA_CONTEXT).when(schemaService).getGlobalContext();
// make test and verify
- 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());
+ final var error = assertError(ar -> restconf.modulesYinGET("module", null, ar));
+ assertEquals("Source module not found", error.getErrorMessage());
+ assertEquals(ErrorType.APPLICATION, error.getErrorType());
+ assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
}
/**
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
// make test and verify
- 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());
+ final var error = assertError(ar -> restconf.modulesYangGET(MOUNT_POINT, "module", null, ar));
+ assertEquals("Source module not found", error.getErrorMessage());
+ assertEquals(ErrorType.APPLICATION, error.getErrorType());
+ assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
}
/**
doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(schemaService).getGlobalContext();
final var error = assertError(
- ar -> restconf.modulesYangGET(NOT_EXISTING_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
+ ar -> restconf.modulesYangGET(NOT_EXISTING_MOUNT_POINT, "module1-behind-mount-point", "2014-02-03", 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());
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.List;
import java.util.Optional;
-import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-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;
class RestconfSchemaSourceUrlProviderTest {
- private static final String URL_PREFIX = "/" + URLConstants.BASE_PATH + "/modules";
-
@Test
@DisplayName("Unsupported module-set name.")
void unsupportedModuleSet() {
void getSchemaSourceUrl(final String moduleName, final Revision revision, final Uri expected) {
final var urlProvider = new RestconfSchemaSourceUrlProvider();
final var result = urlProvider.getSchemaSourceUrl("ODL_modules", moduleName, revision);
- assertTrue(result.isPresent());
assertEquals(Optional.of(expected), result);
}
- private static Stream<Arguments> getSchemaSourceUrlArgs() {
- return Stream.of(
- Arguments.of("odl-module", Revision.of("2023-02-23"), new Uri(URL_PREFIX + "/odl-module/2023-02-23")),
- Arguments.of("module-no-revision", null, new Uri(URL_PREFIX + "/module-no-revision"))
+ private static List<Arguments> getSchemaSourceUrlArgs() {
+ return List.of(
+ Arguments.of("odl-module", Revision.of("2023-02-23"),
+ new Uri("/rests/modules/odl-module?revision=2023-02-23")),
+ Arguments.of("module-no-revision", null, new Uri("/rests/modules/module-no-revision"))
);
}
}