--- /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.common.errors;
+
+import com.google.common.util.concurrent.FutureCallback;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * A {@link FutureCallback} tied to a {@link RestconfFuture}.
+ *
+ * @param <V> value type
+ */
+public abstract class RestconfCallback<V> implements FutureCallback<@NonNull V> {
+ @Override
+ public final void onFailure(final Throwable cause) {
+ onFailure(cause instanceof RestconfDocumentedException rde ? rde
+ : new RestconfDocumentedException("Unexpected failure", cause));
+ }
+
+ protected abstract void onFailure(@NonNull RestconfDocumentedException failure);
+}
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.AbstractFuture;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.ExecutionException;
import org.eclipse.jdt.annotation.NonNull;
return false;
}
+ public final void addCallback(final RestconfCallback<? super V> callback) {
+ Futures.addCallback(this, callback, MoreExecutors.directExecutor());
+ }
+
/**
* Get the result.
*
--- /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.rests.services.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Function;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.core.Response;
+import org.opendaylight.restconf.common.errors.RestconfCallback;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+
+/**
+ * A {@link RestconfCallback} completing an {@link AsyncResponse}.
+ *
+ * @param <V> value type
+ */
+final class JaxRsRestconfCallback<V> extends RestconfCallback<V> {
+ private final Function<V, Response> transform;
+ private final AsyncResponse ar;
+
+ JaxRsRestconfCallback(final AsyncResponse ar, final Function<V, Response> transform) {
+ this.ar = requireNonNull(ar);
+ this.transform = requireNonNull(transform);
+ }
+
+ @Override
+ public void onSuccess(final V result) {
+ ar.resume(transform.apply(result));
+ }
+
+ @Override
+ protected void onFailure(final RestconfDocumentedException failure) {
+ ar.resume(failure);
+ }
+}
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
-import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException;
import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
-import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
final var strategy = server.getRestconfStrategy(reqPath.getSchemaContext(), reqPath.getMountPoint());
- Futures.addCallback(strategy.delete(reqPath.getInstanceIdentifier()), new FutureCallback<>() {
- @Override
- public void onSuccess(final Empty result) {
- ar.resume(Response.noContent().build());
- }
-
- @Override
- public void onFailure(final Throwable failure) {
- ar.resume(failure);
- }
- }, MoreExecutors.directExecutor());
+ strategy.delete(reqPath.getInstanceIdentifier()).addCallback(
+ new JaxRsRestconfCallback<>(ar, ignored -> Response.noContent().build()));
}
/**
private void plainPatchData(final InstanceIdentifierContext reqPath, final ResourceBody body,
final AsyncResponse ar) {
final var req = bindResourceRequest(reqPath, body);
- final var future = req.strategy().merge(req.path(), req.data());
-
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(final Empty result) {
- ar.resume(Response.ok().build());
- }
-
- @Override
- public void onFailure(final Throwable failure) {
- ar.resume(failure);
- }
- }, MoreExecutors.directExecutor());
+ req.strategy().merge(req.path(), req.data()).addCallback(
+ new JaxRsRestconfCallback<>(ar, ignored -> Response.ok().build()));
}
private @NonNull ResourceRequest bindResourceRequest(final InstanceIdentifierContext reqPath,
@VisibleForTesting
void yangPatchData(final @NonNull EffectiveModelContext modelContext,
final @NonNull PatchContext patch, final @Nullable DOMMountPoint mountPoint, final AsyncResponse ar) {
- Futures.addCallback(server.getRestconfStrategy(modelContext, mountPoint).patchData(patch),
- new FutureCallback<>() {
- @Override
- public void onSuccess(final PatchStatusContext result) {
- ar.resume(result);
- }
-
- @Override
- public void onFailure(final Throwable cause) {
- ar.resume(cause);
- }
- }, MoreExecutors.directExecutor());
+ server.getRestconfStrategy(modelContext, mountPoint).patchData(patch)
+ .addCallback(new JaxRsRestconfCallback<>(ar, status -> Response.ok().entity(status).build()));
}
private static @NonNull PatchContext parsePatchBody(final @NonNull EffectiveModelContext context,
import static java.util.Objects.requireNonNull;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
ErrorTag.MALFORMED_MESSAGE, e);
}
- Futures.addCallback(hackInvokeRpc(databind, reqPath, uriInfo, input), new FutureCallback<>() {
- @Override
- public void onSuccess(final Optional<ContainerNode> result) {
- if (result.isPresent()) {
- final var output = result.orElseThrow();
- if (!output.isEmpty()) {
- ar.resume(Response.ok().entity(new NormalizedNodePayload(reqPath.inference(), output)).build());
- return;
- }
- }
- ar.resume(Response.noContent().build());
- }
-
- @Override
- public void onFailure(final Throwable failure) {
- ar.resume(failure);
- }
- }, MoreExecutors.directExecutor());
+ hackInvokeRpc(databind, reqPath, uriInfo, input).addCallback(new JaxRsRestconfCallback<>(ar,
+ result -> result
+ .filter(output -> !output.isEmpty())
+ .map(output -> Response.ok().entity(new NormalizedNodePayload(reqPath.inference(), output)).build())
+ .orElseGet(() -> Response.noContent().build())));
}
private RestconfFuture<Optional<ContainerNode>> hackInvokeRpc(final DatabindContext localDatabind,
*/
package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@Mock
private AsyncResponse asyncResponse;
@Captor
- private ArgumentCaptor<PatchStatusContext> patchStatus;
+ private ArgumentCaptor<Response> responseCaptor;
private RestconfDataServiceImpl dataService;
.when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
doReturn(immediateTrueFluentFuture())
.when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
- doReturn(true).when(asyncResponse).resume(patchStatus.capture());
+ doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
dataService.yangPatchData(JUKEBOX_SCHEMA, patch, null, asyncResponse);
- final var status = patchStatus.getValue();
+ final var response = responseCaptor.getValue();
+ assertEquals(200, response.getStatus());
+ final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+
assertTrue(status.ok());
assertEquals(3, status.editCollection().size());
assertEquals("replace data", status.editCollection().get(1).getEditId());
.when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
doReturn(immediateTrueFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
- doReturn(true).when(asyncResponse).resume(patchStatus.capture());
+ doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
dataService.yangPatchData(JUKEBOX_SCHEMA, patch, mountPoint, asyncResponse);
- final var status = patchStatus.getValue();
+ final var response = responseCaptor.getValue();
+ assertEquals(200, response.getStatus());
+ final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+
assertTrue(status.ok());
assertEquals(3, status.editCollection().size());
assertNull(status.globalErrors());
.when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
doReturn(true).when(readWrite).cancel();
- doReturn(true).when(asyncResponse).resume(patchStatus.capture());
+ doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
dataService.yangPatchData(JUKEBOX_SCHEMA, patch, null, asyncResponse);
- final var status = patchStatus.getValue();
+ final var response = responseCaptor.getValue();
+ assertEquals(200, response.getStatus());
+ final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
assertFalse(status.ok());
assertEquals(3, status.editCollection().size());