Remove RestconfError.ErrorTag
[netconf.git] / restconf / restconf-common / src / main / java / org / opendaylight / restconf / common / ErrorTags.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.common;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableMap;
14 import javax.ws.rs.core.Response.Status;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.opendaylight.yangtools.yang.common.ErrorTag;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  * {@link ErrorTag} mapping to HTTP errors. Aside from the mappings defined by
22  * <a href="https://datatracker.ietf.org/doc/html/rfc8040#section-7">RFC8040 section 7</a>, we also define tags which
23  * map to useful {@link Status} codes.
24  */
25 @Beta
26 @NonNullByDefault
27 public final class ErrorTags {
28     /**
29      * Error reported when the request is valid, but the resource cannot be accessed. This tag typically maps to
30      * {@link Status#SERVICE_UNAVAILABLE}.
31      */
32     // FIXME: redefine as SERVICE_UNAVAILABLE? It would be more obvious
33     public static final ErrorTag RESOURCE_DENIED_TRANSPORT = new ErrorTag("resource-denied-transport");
34
35     private static final Logger LOG = LoggerFactory.getLogger(ErrorTags.class);
36     private static final ImmutableMap<ErrorTag, Status> WELL_KNOWN_ERROR_TAGS = ImmutableMap.<ErrorTag, Status>builder()
37         .put(ErrorTag.IN_USE,                     Status.CONFLICT)
38         .put(ErrorTag.INVALID_VALUE,              Status.BAD_REQUEST)
39         .put(ErrorTag.TOO_BIG,                    Status.REQUEST_ENTITY_TOO_LARGE)
40         .put(ErrorTag.MISSING_ATTRIBUTE,          Status.BAD_REQUEST)
41         .put(ErrorTag.BAD_ATTRIBUTE,              Status.BAD_REQUEST)
42         .put(ErrorTag.UNKNOWN_ATTRIBUTE,          Status.BAD_REQUEST)
43         .put(ErrorTag.MISSING_ELEMENT,            Status.BAD_REQUEST)
44         .put(ErrorTag.BAD_ELEMENT,                Status.BAD_REQUEST)
45         .put(ErrorTag.UNKNOWN_ELEMENT,            Status.BAD_REQUEST)
46         .put(ErrorTag.UNKNOWN_NAMESPACE,          Status.BAD_REQUEST)
47
48         .put(ErrorTag.ACCESS_DENIED,              Status.FORBIDDEN)
49         .put(ErrorTag.LOCK_DENIED,                Status.CONFLICT)
50         .put(ErrorTag.RESOURCE_DENIED,            Status.CONFLICT)
51         .put(ErrorTag.ROLLBACK_FAILED,            Status.INTERNAL_SERVER_ERROR)
52         .put(ErrorTag.DATA_EXISTS,                Status.CONFLICT)
53         .put(ErrorTag.DATA_MISSING,               dataMissingHttpStatus())
54
55         .put(ErrorTag.OPERATION_NOT_SUPPORTED,    Status.NOT_IMPLEMENTED)
56         .put(ErrorTag.OPERATION_FAILED,           Status.INTERNAL_SERVER_ERROR)
57         .put(ErrorTag.PARTIAL_OPERATION,          Status.INTERNAL_SERVER_ERROR)
58         .put(ErrorTag.MALFORMED_MESSAGE,          Status.BAD_REQUEST)
59         .put(ErrorTags.RESOURCE_DENIED_TRANSPORT, Status.SERVICE_UNAVAILABLE)
60         .build();
61
62     private ErrorTags() {
63         // Hidden on purpose
64     }
65
66     /**
67      * Return the HTTP {@link Status} corresponding to specified {@link ErrorTag}.
68      *
69      * @param tag Error tag to map
70      * @return HTTP Status
71      * @throws NullPointerException if {@code tag} is null
72      */
73     public static Status statusOf(final ErrorTag tag) {
74         final Status known = WELL_KNOWN_ERROR_TAGS.get(requireNonNull(tag));
75         return known != null ? known : Status.INTERNAL_SERVER_ERROR;
76     }
77
78     private static Status dataMissingHttpStatus() {
79         // Control over the HTTP status reported on "data-missing" conditions. This defaults to disabled,
80         // HTTP status 409 as specified by RFC8040 (and all previous drafts). See the discussion in:
81         // https://www.rfc-editor.org/errata/eid5565
82         // https://mailarchive.ietf.org/arch/msg/netconf/hkVDdHK4xA74NgvXzWP0zObMiyY/
83         final String propName = "org.opendaylight.restconf.eid5565";
84         final String propValue = System.getProperty(propName, "disabled");
85         switch (propValue) {
86             case "enabled":
87                 // RFC7231 interpretation: 404 Not Found
88                 LOG.info("RESTCONF data-missing condition is reported as HTTP status 404 (Errata 5565)");
89                 return Status.NOT_FOUND;
90             case "disabled":
91                 break;
92             default:
93                 LOG.warn("Unhandled {} value \"{}\", assuming disabled", propName, propValue);
94         }
95
96         // RFC8040 specification: 409 Conflict
97         return Status.CONFLICT;
98     }
99 }