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