All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.restconf.server.spi.AbstractServerStrategy Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
/*
 * Copyright (c) 2024 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.spi;

import static java.util.Objects.requireNonNull;

import com.google.common.annotations.Beta;
import java.net.URI;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.api.FormattableBody;
import org.opendaylight.restconf.server.api.ChildBody;
import org.opendaylight.restconf.server.api.ChildBody.PrefixAndBody;
import org.opendaylight.restconf.server.api.CreateResourceResult;
import org.opendaylight.restconf.server.api.DataGetParams;
import org.opendaylight.restconf.server.api.DataGetResult;
import org.opendaylight.restconf.server.api.DataPatchResult;
import org.opendaylight.restconf.server.api.DataPostBody;
import org.opendaylight.restconf.server.api.DataPostResult;
import org.opendaylight.restconf.server.api.DataPutResult;
import org.opendaylight.restconf.server.api.DataYangPatchResult;
import org.opendaylight.restconf.server.api.DatabindContext;
import org.opendaylight.restconf.server.api.DatabindPath;
import org.opendaylight.restconf.server.api.DatabindPath.Action;
import org.opendaylight.restconf.server.api.DatabindPath.Data;
import org.opendaylight.restconf.server.api.DatabindPath.InstanceReference;
import org.opendaylight.restconf.server.api.DatabindPath.Rpc;
import org.opendaylight.restconf.server.api.InvokeResult;
import org.opendaylight.restconf.server.api.ModulesGetResult;
import org.opendaylight.restconf.server.api.OperationInputBody;
import org.opendaylight.restconf.server.api.PatchBody;
import org.opendaylight.restconf.server.api.PatchContext;
import org.opendaylight.restconf.server.api.ResourceBody;
import org.opendaylight.restconf.server.api.ServerException;
import org.opendaylight.restconf.server.api.ServerRequest;
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.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;

/**
 * Abstract base class for {@link ServerStrategy} implementations based on {@link ServerActionOperations}.
 */
@Beta
public abstract class AbstractServerStrategy implements ServerStrategy {
    protected final @NonNull ApiPathNormalizer pathNormalizer;
    protected final @NonNull DatabindContext databind;
    private final @NonNull Data emptyPath;
    private final HttpGetResource operations;

    protected AbstractServerStrategy(final DatabindContext databind) {
        this.databind = requireNonNull(databind);
        pathNormalizer = new ApiPathNormalizer(databind);
        emptyPath = new DatabindPath.Data(databind);
        operations = new OperationsResource(pathNormalizer);
    }

    protected abstract @NonNull ServerActionOperations action();

    protected abstract @NonNull ServerDataOperations data();

    protected abstract @NonNull ServerModulesOperations modules();

    protected abstract @NonNull ServerRpcOperations rpc();

    protected abstract @NonNull ServerMountPointResolver resolver();

    @Override
    public final void dataDELETE(final ServerRequest request, final ApiPath apiPath) {
        final Data path;
        try {
            path = pathNormalizer.normalizeDataPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        final var instance = path.instance();
        if (instance.isEmpty()) {
            request.completeWith(new ServerException(ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED,
                "Cannot delete a datastore"));
            return;
        }
        data().deleteData(request, path);
    }

    @Override
    public final void dataGET(final ServerRequest request) {
        dataGET(request, emptyPath);
    }

    @Override
    public final void dataGET(final ServerRequest request, final ApiPath apiPath) {
        final Data path;
        try {
            path = pathNormalizer.normalizeDataPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        dataGET(request, path);
    }

    @NonNullByDefault
    private void dataGET(final ServerRequest request, final Data path) {
        final DataGetParams params;
        try {
            params = DataGetParams.of(request.queryParameters());
        } catch (IllegalArgumentException e) {
            request.completeWith(new ServerException(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
                "Invalid GET /data parameters", e));
            return;
        }
        data().getData(request, path, params);
    }

    @Override
    public final void dataPATCH(final ServerRequest request, final ResourceBody body) {
        dataPATCH(request, emptyPath, body);
    }

    @Override
    public final void dataPATCH(final ServerRequest request, final ApiPath apiPath,
            final ResourceBody body) {
        final Data path;
        try {
            path = pathNormalizer.normalizeDataPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        dataPATCH(request, path, body);
    }

    private void dataPATCH(final ServerRequest request, final Data path, final ResourceBody body) {
        final NormalizedNode data;
        try {
            data = body.toNormalizedNode(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        data().mergeData(request, path, data);
    }

    @Override
    public final void dataPATCH(final ServerRequest request, final PatchBody body) {
        dataPATCH(request, emptyPath, body);
    }

    @Override
    public final void dataPATCH(final ServerRequest request, final ApiPath apiPath,
            final PatchBody body) {
        final Data path;
        try {
            path = pathNormalizer.normalizeDataPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        dataPATCH(request, path, body);
    }

    private void dataPATCH(final ServerRequest request, final Data path, final PatchBody body) {
        final PatchContext patch;
        try {
            patch = body.toPatchContext(new DefaultResourceContext(path));
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        data().patchData(request, path, patch);
    }

    @Override
    public final void dataPOST(final ServerRequest request, final ChildBody body) {
        dataCreate(request, emptyPath, body);
    }

    @Override
    public final void dataPOST(final ServerRequest request, final ApiPath path,
            final DataPostBody body) {
        final InstanceReference ref;
        try {
            ref = pathNormalizer.normalizeDataOrActionPath(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }

        switch (ref) {
            case Action actionPath -> {
                try (var inputBody = body.toOperationInput()) {
                    dataInvoke(request, actionPath, inputBody);
                }
            }
            case Data dataPath -> {
                try (var resourceBody = body.toResource()) {
                    dataCreate(request, dataPath, resourceBody);
                }
            }
        }
    }

    private void dataCreate(final ServerRequest request, final Data path,
            final ChildBody body) {
        final Insert insert;
        try {
            insert = Insert.of(path.databind(), request.queryParameters());
        } catch (IllegalArgumentException e) {
            request.completeWith(new ServerException(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e));
            return;
        }
        final PrefixAndBody payload;
        try {
            payload = body.toPayload(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }

        if (insert != null) {
            data().createData(request, path, insert, payload);
        } else {
            data().createData(request, path, payload);
        }
    }

    @NonNullByDefault
    private void dataInvoke(final ServerRequest request, final Action path,
            final OperationInputBody body) {
        final ContainerNode input;
        try {
            input = body.toContainerNode(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        action().invokeAction(request, path, input);
    }

    @Override
    public final void dataPUT(final ServerRequest request, final ResourceBody body) {
        dataPUT(request, emptyPath, body);
    }

    @Override
    public final void dataPUT(final ServerRequest request, final ApiPath apiPath,
            final ResourceBody body) {
        final Data path;
        try {
            path = pathNormalizer.normalizeDataPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        dataPUT(request, path, body);
    }

    @NonNullByDefault
    private void dataPUT(final ServerRequest request, final Data path, final ResourceBody body) {
        final Insert insert;
        try {
            insert = Insert.of(databind, request.queryParameters());
        } catch (IllegalArgumentException e) {
            request.completeWith(new ServerException(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e));
            return;
        }
        final NormalizedNode data;
        try {
            data = body.toNormalizedNode(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }

        if (insert != null) {
            data().putData(request, path, insert, data);
        } else {
            data().putData(request, path, data);
        }
    }

    @Override
    public final void operationsGET(final ServerRequest request) {
        operations.httpGET(request);
    }

    @Override
    public final void operationsGET(final ServerRequest request, final ApiPath apiPath) {
        operations.httpGET(request, apiPath);
    }

    @Override
    public final void operationsPOST(final ServerRequest request, final URI restconfURI,
            final ApiPath apiPath, final OperationInputBody body) {
        final Rpc path;
        try {
            path = pathNormalizer.normalizeRpcPath(apiPath);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }

        final ContainerNode input;
        try {
            input = body.toContainerNode(path);
        } catch (ServerException e) {
            request.completeWith(e);
            return;
        }
        rpc().invokeRpc(request, restconfURI, path, input);
    }

    @Override
    public final void modulesGET(final ServerRequest request, final SourceIdentifier source,
            final Class representation) {
        modules().getModelSource(request, source, representation);
    }

    @Override
    public final StrategyAndPath resolveStrategy(final ApiPath path) throws ServerException {
        var mount = path.indexOf("yang-ext", "mount");
        return mount == -1 ? new StrategyAndPath(this, path)
            : resolver().resolveMountPoint(pathNormalizer.normalizeDataPath(path.subPath(0, mount)))
                .resolveStrategy(path.subPath(mount + 1));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy