@Suspended final AsyncResponse ar) {
server.dataDELETE(identifier.apiPath).addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- Response transform(final Empty result) {
+ protected Response transform(final Empty result) {
return Response.noContent().build();
}
});
final ReadDataParams readParams, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
- Response transform(final NormalizedNodePayload result) {
+ protected 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
- Response transform(final Empty result) {
+ protected 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
- Response transform(final PatchStatusContext result) {
+ protected Response transform(final PatchStatusContext result) {
return Response.status(statusOf(result)).entity(result).build();
}
final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<DataPostResult>(ar) {
@Override
- Response transform(final DataPostResult result) {
+ protected 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
- Response transform(final DataPutResult result) {
+ protected 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
- Response transform(final OperationsGetResult result) {
+ protected 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
- Response transform(final OperationOutput result) {
+ protected 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
- Response transform(final NormalizedNodePayload result) {
+ protected Response transform(final NormalizedNodePayload result) {
return Response.ok().entity(result).build();
}
});
*
* @param <V> value type
*/
-abstract class JaxRsRestconfCallback<V> extends RestconfCallback<V> {
+// FIXME: hide this class
+public abstract class JaxRsRestconfCallback<V> extends RestconfCallback<V> {
private final AsyncResponse ar;
- JaxRsRestconfCallback(final AsyncResponse ar) {
+ // FIXME: hide this constructor
+ protected JaxRsRestconfCallback(final AsyncResponse ar) {
this.ar = requireNonNull(ar);
}
ar.resume(failure);
}
- abstract Response transform(V result);
+ // FIXME: hide this method and its implementations
+ protected abstract Response transform(V result);
}
package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import java.time.format.DateTimeParseException;
import java.util.Date;
-import java.util.Iterator;
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 org.opendaylight.mdsal.dom.api.DOMMountPoint;
+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.YangNames;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
/**
* Retrieval of the YANG modules which server supports.
* Get schema of specific module.
*
* @param identifier path parameter
- * @return {@link SchemaExportContext}
+ * @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 SchemaExportContext getSchema(@PathParam("identifier") final String identifier) {
- return toSchemaExportContextFromIdentifier(schemaService.getGlobalContext(), identifier, mountPointService,
- sourceProvider);
+ 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();
+ }
+ });
}
/**
* @return {@link SchemaExportContext}
*/
@VisibleForTesting
- static SchemaExportContext toSchemaExportContextFromIdentifier(final EffectiveModelContext schemaContext,
- final String identifier, final DOMMountPointService domMountPointService,
- final DOMYangTextSourceProvider sourceProvider) {
+ 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 componentIter = pathComponents.iterator();
- if (!Iterables.contains(pathComponents, MOUNT)) {
- final var module = coerceModule(schemaContext, validateAndGetModulName(componentIter),
- validateAndGetRevision(componentIter), null);
- return new SchemaExportContext(schemaContext, module, sourceProvider);
- }
- final var pathBuilder = new StringBuilder();
- while (componentIter.hasNext()) {
- final var current = componentIter.next();
- if (MOUNT.equals(current)) {
- pathBuilder.append('/').append(MOUNT);
- break;
+ 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('/');
}
- if (!pathBuilder.isEmpty()) {
- pathBuilder.append('/');
+ final InstanceIdentifierContext point;
+ try {
+ point = ParserIdentifier.toInstanceIdentifier(sb.toString(), schemaContext,
+ requireNonNull(domMountPointService));
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
}
- pathBuilder.append(current);
- }
- final var point = ParserIdentifier.toInstanceIdentifier(pathBuilder.toString(), schemaContext,
- requireNonNull(domMountPointService));
- final var context = coerceModelContext(point.getMountPoint());
- final var module = coerceModule(context, validateAndGetModulName(componentIter),
- validateAndGetRevision(componentIter), point.getMountPoint());
- return new SchemaExportContext(context, module, sourceProvider);
- }
-
- /**
- * Validation and parsing of revision.
- *
- * @param revisionDate iterator
- * @return A Revision
- */
- @VisibleForTesting
- static Revision validateAndGetRevision(final Iterator<String> revisionDate) {
- if (!revisionDate.hasNext()) {
- throw new RestconfDocumentedException("Revision date must be supplied.",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
- }
- try {
- return Revision.of(revisionDate.next());
- } catch (final DateTimeParseException e) {
- throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", 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";
}
- }
- /**
- * Validation of name.
- *
- * @param moduleName iterator
- * @return {@link String}
- */
- @VisibleForTesting
- static String validateAndGetModulName(final Iterator<String> moduleName) {
- if (!moduleName.hasNext()) {
- throw new RestconfDocumentedException("Module name must be supplied.",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ // 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 name = moduleName.next();
- if (name.isEmpty() || !YangNames.IDENTIFIER_START.matches(name.charAt(0))) {
- throw new RestconfDocumentedException("Identifier must start with character from set 'a-zA-Z_",
- 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 (name.toUpperCase(Locale.ROOT).startsWith("XML")) {
- throw new RestconfDocumentedException("Identifier must NOT start with XML ignore case.",
- 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(name.substring(1))) {
- throw new RestconfDocumentedException("Supplied name has not expected identifier format.",
- 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));
}
- return name;
- }
- private static ModuleEffectiveStatement coerceModule(final EffectiveModelContext context, final String moduleName,
- final Revision revision, final DOMMountPoint mountPoint) {
- return context.findModule(moduleName, revision)
- .map(Module::asEffectiveStatement)
- .orElseThrow(() -> {
- final var msg = "Module %s %s cannot be found on %s.".formatted(moduleName, revision,
- mountPoint == null ? "controller" : mountPoint.getIdentifier());
- return new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
- });
- }
+ // 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));
+ }
- private static EffectiveModelContext coerceModelContext(final DOMMountPoint mountPoint) {
- final EffectiveModelContext context = modelContext(mountPoint);
- checkState(context != null, "Mount point %s does not have a model context", mountPoint);
- return context;
- }
+ 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));
+ }
- private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
- return mountPoint.getService(DOMSchemaService.class)
- .map(DOMSchemaService::getGlobalContext)
- .orElse(null);
+ return RestconfFuture.of(new SchemaExportContext(context, optModule.orElseThrow().asEffectiveStatement(),
+ // FIXME: this does not seem right -- mounts should have their own thing
+ sourceProvider));
}
}
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+// FIXME: hide this class
@ExtendWith(MockitoExtension.class)
-abstract class AbstractRestconfTest extends AbstractJukeboxTest {
+public abstract class AbstractRestconfTest extends AbstractJukeboxTest {
static final JaxRsApiPath JUKEBOX_API_PATH = new JaxRsApiPath("example-jukebox:jukebox");
@Mock
return assertEntity(NormalizedNodePayload.class, status, invocation).data();
}
- static final <T> T assertEntity(final Class<T> expectedType, final int expectedStatus,
+ // FIXME: hide this method
+ public static final <T> T assertEntity(final Class<T> expectedType, final int expectedStatus,
final Consumer<AsyncResponse> invocation) {
return assertInstanceOf(expectedType, assertEntity(expectedStatus, invocation));
}
return assertResponse(expectedStatus, invocation).getEntity();
}
- static final RestconfError assertError(final Consumer<AsyncResponse> invocation) {
+ // FIXME: hide this method
+ public static final RestconfError assertError(final Consumer<AsyncResponse> invocation) {
final var errors = assertErrors(invocation);
assertEquals(1, errors.size());
final var error = errors.get(0);
return error;
}
- static final List<RestconfError> assertErrors(final Consumer<AsyncResponse> invocation) {
+ // FIXME: hide this method
+ public static final List<RestconfError> assertErrors(final Consumer<AsyncResponse> invocation) {
final var ar = mock(AsyncResponse.class);
final var captor = ArgumentCaptor.forClass(RestconfDocumentedException.class);
doReturn(true).when(ar).resume(captor.capture());
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.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;
*/
@Test
void toSchemaExportContextFromIdentifierTest() {
- final var exportContext = RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(
- MODEL_CONTEXT, TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, null, sourceProvider);
- assertNotNull(exportContext);
-
+ 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());
void toSchemaExportContextFromIdentifierNotFoundTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(
- MODEL_CONTEXT, "not-existing-module" + "/" + "2016-01-01", null, sourceProvider));
+ 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);
void toSchemaExportContextFromIdentifierInvalidIdentifierNegativeTest() {
final var ex = assertThrows(RestconfDocumentedException.class,
() -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
- TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, null, sourceProvider));
+ 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 exportContext = RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
MOUNT_POINT_IDENT + "/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION,
- mountPointService, sourceProvider);
+ mountPointService, sourceProvider).getOrThrow();
final var module = exportContext.module();
assertEquals(TEST_MODULE_NAME, module.argument().getLocalName());
final var ex = assertThrows(RestconfDocumentedException.class,
() -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
MOUNT_POINT_IDENT + "/" + "not-existing-module" + "/" + "2016-01-01",
- mountPointService, sourceProvider));
+ mountPointService, sourceProvider)
+ .getOrThrow());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
final var error = errors.get(0);
final var ex = assertThrows(RestconfDocumentedException.class,
() -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
MOUNT_POINT_IDENT + "/" + TEST_MODULE_REVISION + "/" + TEST_MODULE_NAME, mountPointService,
- sourceProvider));
+ sourceProvider).getOrThrow());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
final var error = errors.get(0);
final var ex = assertThrows(RestconfDocumentedException.class,
() -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
"/yang-ext:mount/" + TEST_MODULE_NAME + "/" + TEST_MODULE_REVISION, mountPointService,
- sourceProvider));
+ sourceProvider).getOrThrow());
final var errors = ex.getErrors();
assertEquals(1, errors.size());
final var error = errors.get(0);
.getService(DOMSchemaService.class);
doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(MOUNT_IID);
}
+
+ /**
+ * Negative test of module revision validation when there is no revision. Test fails catching
+ * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
+ */
+ @Test
+ void validateAndGetRevisionNotSuppliedTest() {
+ final var error = assertInvalidValue(
+ () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, "module", null, null)
+ .getOrThrow());
+ assertEquals("Revision date must be supplied.", error.getErrorMessage());
+ }
+
+ /**
+ * Negative test of module revision validation when supplied revision is not parsable as revision. Test fails
+ * catching <code>RestconfDocumentedException</code>.
+ */
+ @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);
+ 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());
+ }
+
+ /**
+ * Negative test of module name validation when there is no module name. Test fails catching
+ * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
+ */
+ @Test
+ void validateAndGetModulNameNotSuppliedTest() {
+ mockMountPoint();
+
+ final var error = assertInvalidValue(
+ () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, MOUNT_POINT_IDENT,
+ mountPointService, null).getOrThrow());
+ assertEquals("Module name must be supplied", error.getErrorMessage());
+ }
+
+ /**
+ * Negative test of module name validation when supplied name is not parsable as module name on the first
+ * character. Test fails catching <code>RestconfDocumentedException</code> and checking for correct error type,
+ * error tag and error status code.
+ */
+ @Test
+ void validateAndGetModuleNameNotParsableFirstTest() {
+ final var error = assertInvalidValue(
+ () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
+ "01-not-parsable-as-name-on-first-char", null, null).getOrThrow());
+ assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
+ }
+
+ /**
+ * Negative test of module name validation when supplied name is not parsable as module name on any of the
+ * characters after the first character. Test fails catching <code>RestconfDocumentedException</code> and checking
+ * for correct error type, error tag and error status code.
+ */
+ @Test
+ public void validateAndGetModuleNameNotParsableNextTest() {
+ final var error = assertInvalidValue(
+ () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT,
+ "not-parsable-as-name-after-first-char*", null, null).getOrThrow());
+ assertEquals("Supplied name has not expected identifier format", error.getErrorMessage());
+ }
+
+ /**
+ * Negative test of module name validation when supplied name is empty. Test fails catching
+ * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
+ */
+ @Test
+ void validateAndGetModuleNameEmptyTest() {
+ final var error = assertInvalidValue(
+ () -> RestconfSchemaServiceImpl.toSchemaExportContextFromIdentifier(MODEL_CONTEXT, "", null, null)
+ .getOrThrow());
+ 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);
+ assertEquals(ErrorType.PROTOCOL, error.getErrorType());
+ assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
+ return error;
+ }
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.when;
+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 org.junit.Before;
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.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
+import org.opendaylight.restconf.nb.rfc8040.legacy.SchemaExportContext;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
.createMountPoint(YangInstanceIdentifier.of(QName.create("mount:point:2", "2016-01-01", "cont")))
.register();
- when(mockSchemaService.getExtensions())
- .thenReturn(ImmutableClassToInstanceMap.of(DOMYangTextSourceProvider.class, mockSourceProvider));
+ doReturn(ImmutableClassToInstanceMap.of(DOMYangTextSourceProvider.class, mockSourceProvider))
+ .when(mockSchemaService).getExtensions();
schemaService = new RestconfSchemaServiceImpl(mockSchemaService, mountPointService);
}
@Test
public void getSchemaTest() {
// prepare conditions - return not-mount point schema context
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
// make test
- final var exportContext = schemaService.getSchema(TEST_MODULE);
+ final var exportContext = assertEntity(SchemaExportContext.class, 200,
+ ar -> schemaService.getSchema(TEST_MODULE, ar));
// verify
assertNotNull(exportContext);
@Test
public void getSchemaForNotExistingModuleTest() {
// prepare conditions - return not-mount point schema context
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
-
- // make test & verify
- final var ex = assertThrows(RestconfDocumentedException.class, () ->
- schemaService.getSchema(NOT_EXISTING_MODULE));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).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());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
@Test
public void getSchemaMountPointTest() {
// prepare conditions - return schema context with mount points
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test
- final var exportContext = schemaService.getSchema(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT);
+ final var exportContext = assertEntity(SchemaExportContext.class, 200,
+ ar -> schemaService.getSchema(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT, ar));
// verify
assertNotNull(exportContext);
@Test
public void getSchemaForNotExistingModuleMountPointTest() {
// prepare conditions - return schema context with mount points
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
-
- // make test & verify
- final var ex = assertThrows(RestconfDocumentedException.class, () ->
- schemaService.getSchema(MOUNT_POINT + NOT_EXISTING_MODULE));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).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());
assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
assertEquals(ErrorType.APPLICATION, error.getErrorType());
}
- /**
- * Try to get schema with <code>null</code> <code>SchemaContext</code> expecting <code>NullPointerException</code>.
- */
- @Test
- public void getSchemaWithNullSchemaContextTest() {
- // prepare conditions - returned schema context is null
- when(mockSchemaService.getGlobalContext()).thenReturn(null);
-
- // make test
- assertThrows(NullPointerException.class, () -> schemaService.getSchema(TEST_MODULE));
- }
-
- /**
- * Try to get schema with <code>null</code> <code>SchemaContext</code> for mount points.
- * <code>NullPointerException</code> is expected.
- */
- @Test
- public void getSchemaWithNullSchemaContextMountPointTest() {
- // prepare conditions - returned schema context for mount points is null
- when(mockSchemaService.getGlobalContext()).thenReturn(null);
-
- // make test
- assertThrows(NullPointerException.class,
- () -> schemaService.getSchema(MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT));
- }
-
/**
* Try to get schema with <code>null</code> <code>SchemaContext</code> behind mount point when using
* <code>NULL_MOUNT_POINT</code>. Test is expected to fail with <code>NullPointerException</code>.
@Test
public void getSchemaNullSchemaContextBehindMountPointTest() {
// prepare conditions - return correct schema context for mount points (this is not null)
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test - call service on mount point with null schema context
- final var errors = assertThrows(RestconfDocumentedException.class,
- // NULL_MOUNT_POINT contains null schema context
- () -> schemaService.getSchema(NULL_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT))
- .getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ // NULL_MOUNT_POINT contains null schema context
+ final var error = assertError(
+ ar -> schemaService.getSchema(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());
}
- /**
- * Try to get schema with null identifier expecting <code>NullPointerException</code>. The same processing is for
- * server and also for mount point.
- */
- @Test
- public void getSchemaWithNullIdentifierTest() {
- // prepare conditions - return correct schema context
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
-
- // make test
- assertThrows(NullPointerException.class, () -> schemaService.getSchema(null));
- }
-
/**
* Try to get schema with empty (not valid) identifier catching <code>RestconfDocumentedException</code>. Error
* type, error tag and error status code are compared to expected values.
@Test
public void getSchemaWithEmptyIdentifierTest() {
// prepare conditions - return correct schema context
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
- // make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class, () -> schemaService.getSchema(""));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema("", 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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class, () -> schemaService.getSchema(MOUNT_POINT + ""));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema(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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> schemaService.getSchema("01_module/2016-01-01"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema("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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> schemaService.getSchema(MOUNT_POINT + "01_module/2016-01-01"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema(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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class, () -> schemaService.getSchema("2014-01-01"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema("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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> schemaService.getSchema(MOUNT_POINT + "2014-01-01"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema(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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT);
+ doReturn(SCHEMA_CONTEXT).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class, () -> schemaService.getSchema("module"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema("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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
// make test and verify
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> schemaService.getSchema(MOUNT_POINT + "module"));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(ar -> schemaService.getSchema(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
- when(mockSchemaService.getGlobalContext()).thenReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS);
+ doReturn(SCHEMA_CONTEXT_WITH_MOUNT_POINTS).when(mockSchemaService).getGlobalContext();
- // make test
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> schemaService.getSchema(NOT_EXISTING_MOUNT_POINT + TEST_MODULE_BEHIND_MOUNT_POINT));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
+ final var error = assertError(
+ ar -> schemaService.getSchema(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());
+++ /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 org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import com.google.common.collect.Iterators;
-import java.util.Collections;
-import java.util.List;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError;
-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;
-
-/**
- * Unit test for {@link ParserIdentifier}'s validate methods.
- */
-class RestconfValidationTest {
- private static final List<String> REVISIONS = List.of("2014-01-01", "2015-01-01", "2016-01-01");
- private static final List<String> NAMES = List.of("_module-1", "_module-2", "_module-3");
-
- /**
- * Test of successful validation of module revision.
- */
- @Test
- void validateAndGetRevisionTest() {
- assertEquals(Revision.of("2014-01-01"), RestconfSchemaServiceImpl.validateAndGetRevision(REVISIONS.iterator()));
- }
-
- /**
- * Negative test of module revision validation when there is no revision. Test fails catching
- * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
- */
- @Test
- void validateAndGetRevisionNotSuppliedTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetRevision(Collections.emptyIterator()));
- assertEquals("Revision date must be supplied.", error.getErrorMessage());
- }
-
- /**
- * Negative test of module revision validation when supplied revision is not parsable as revision. Test fails
- * catching <code>RestconfDocumentedException</code>.
- */
- @Test
- void validateAndGetRevisionNotParsableTest() {
- final var ex = assertThrows(RestconfDocumentedException.class,
- () -> RestconfSchemaServiceImpl.validateAndGetRevision(Iterators.singletonIterator(
- "not-parsable-as-date")));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- final var error = errors.get(0);
- 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 of successful validation of module name.
- */
- @Test
- void validateAndGetModulNameTest() {
- assertEquals("_module-1", RestconfSchemaServiceImpl.validateAndGetModulName(NAMES.iterator()));
- }
-
- /**
- * Negative test of module name validation when there is no module name. Test fails catching
- * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
- */
- @Test
- void validateAndGetModulNameNotSuppliedTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetModulName(Collections.emptyIterator()));
- assertEquals("Module name must be supplied.", error.getErrorMessage());
- }
-
- /**
- * Negative test of module name validation when supplied name is not parsable as module name on the first
- * character. Test fails catching <code>RestconfDocumentedException</code> and checking for correct error type,
- * error tag and error status code.
- */
- @Test
- void validateAndGetModuleNameNotParsableFirstTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetModulName(Iterators.singletonIterator(
- "01-not-parsable-as-name-on-firts-char")));
- assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
- }
-
- /**
- * Negative test of module name validation when supplied name is not parsable as module name on any of the
- * characters after the first character. Test fails catching <code>RestconfDocumentedException</code> and checking
- * for correct error type, error tag and error status code.
- */
- @Test
- public void validateAndGetModuleNameNotParsableNextTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetModulName(Iterators.singletonIterator(
- "not-parsable-as-name-after-first-char*")));
- assertEquals("Supplied name has not expected identifier format.", error.getErrorMessage());
- }
-
- /**
- * Negative test of module name validation when supplied name begins with 'XML' ignore case. Test fails catching
- * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
- */
- @Test
- void validateAndGetModuleNameNotParsableXmlTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetModulName(Iterators.singletonIterator("xMl-module-name")));
- assertEquals("Identifier must NOT start with XML ignore case.", error.getErrorMessage());
- }
-
- /**
- * Negative test of module name validation when supplied name is empty. Test fails catching
- * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
- */
- @Test
- void validateAndGetModuleNameEmptyTest() {
- final var error = assertInvalidValue(
- () -> RestconfSchemaServiceImpl.validateAndGetModulName(Iterators.singletonIterator("")));
- 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);
- assertEquals(ErrorType.PROTOCOL, error.getErrorType());
- assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
- return error;
- }
-}