aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/restapi/RestApi.java
blob: 555ebecec576d906cf8dc8fa04a0f473786e21f9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.restapi;

import ai.vespa.http.HttpURL;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.container.jdisc.AclMapping;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.RequestHandlerSpec;
import com.yahoo.container.jdisc.RequestView;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.security.tls.Capability;
import com.yahoo.security.tls.CapabilitySet;
import com.yahoo.security.tls.ConnectionAuthContext;

import javax.net.ssl.SSLSession;
import java.io.InputStream;
import java.security.Principal;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalLong;

/**
 * Rest API routing and response serialization
 *
 * @author bjorncs
 */
public interface RestApi {

    static Builder builder() { return new RestApiImpl.BuilderImpl(); }
    static RouteBuilder route(String pathPattern) { return new RestApiImpl.RouteBuilderImpl(pathPattern); }
    static HandlerConfigBuilder handlerConfig() { return new RestApiImpl.HandlerConfigBuilderImpl(); }

    HttpResponse handleRequest(HttpRequest request);
    ObjectMapper jacksonJsonMapper();

    /** @see com.yahoo.container.jdisc.HttpRequestHandler#requestHandlerSpec() */
    RequestHandlerSpec requestHandlerSpec();

    /** @see com.yahoo.container.jdisc.utils.CapabilityRequiringRequestHandler */
    CapabilitySet requiredCapabilities(RequestView req);

    interface Builder {
        Builder setObjectMapper(ObjectMapper mapper);
        Builder setDefaultRoute(RouteBuilder route);
        Builder addRoute(RouteBuilder route);
        Builder addFilter(Filter filter);
        /** see {@link RestApiMappers#DEFAULT_EXCEPTION_MAPPERS} for default mappers */
        <EXCEPTION extends RuntimeException> Builder addExceptionMapper(Class<EXCEPTION> type, ExceptionMapper<EXCEPTION> mapper);
        /** see {@link RestApiMappers#DEFAULT_RESPONSE_MAPPERS} for default mappers */
        <RESPONSE_ENTITY> Builder addResponseMapper(Class<RESPONSE_ENTITY> type, ResponseMapper<RESPONSE_ENTITY> mapper);
        /** see {@link RestApiMappers#DEFAULT_REQUEST_MAPPERS} for default mappers */
        <REQUEST_ENTITY> Builder addRequestMapper(Class<REQUEST_ENTITY> type, RequestMapper<REQUEST_ENTITY> mapper);
        <RESPONSE_ENTITY> Builder registerJacksonResponseEntity(Class<RESPONSE_ENTITY> type);
        <REQUEST_ENTITY> Builder registerJacksonRequestEntity(Class<REQUEST_ENTITY> type);
        /** Disables mappers listed in {@link RestApiMappers#DEFAULT_EXCEPTION_MAPPERS} */
        Builder disableDefaultExceptionMappers();
        /** Disables mappers listed in {@link RestApiMappers#DEFAULT_RESPONSE_MAPPERS} */
        Builder disableDefaultResponseMappers();
        Builder disableDefaultAclMapping();
        Builder requiredCapabilities(Capability... capabilities);
        Builder requiredCapabilities(CapabilitySet capabilities);
        RestApi build();
    }

    interface RouteBuilder {
        RouteBuilder name(String name);
        RouteBuilder requiredCapabilities(Capability... capabilities);
        RouteBuilder requiredCapabilities(CapabilitySet capabilities);
        RouteBuilder addFilter(Filter filter);

        // GET
        RouteBuilder get(Handler<?> handler);
        RouteBuilder get(Handler<?> handler, HandlerConfigBuilder config);

        // POST
        RouteBuilder post(Handler<?> handler);
        <REQUEST_ENTITY> RouteBuilder post(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler);
        RouteBuilder post(Handler<?> handler, HandlerConfigBuilder config);
        <REQUEST_ENTITY> RouteBuilder post(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler, HandlerConfigBuilder config);

        // PUT
        RouteBuilder put(Handler<?> handler);
        <REQUEST_ENTITY> RouteBuilder put(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler);
        RouteBuilder put(Handler<?> handler, HandlerConfigBuilder config);
        <REQUEST_ENTITY> RouteBuilder put(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler, HandlerConfigBuilder config);

        // DELETE
        RouteBuilder delete(Handler<?> handler);
        RouteBuilder delete(Handler<?> handler, HandlerConfigBuilder config);

        // PATCH
        RouteBuilder patch(Handler<?> handler);
        <REQUEST_ENTITY> RouteBuilder patch(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler);
        RouteBuilder patch(Handler<?> handler, HandlerConfigBuilder config);
        <REQUEST_ENTITY> RouteBuilder patch(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler, HandlerConfigBuilder config);

        // Default
        RouteBuilder defaultHandler(Handler<?> handler);
        RouteBuilder defaultHandler(Handler<?> handler, HandlerConfigBuilder config);
        <REQUEST_ENTITY> RouteBuilder defaultHandler(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler);
        <REQUEST_ENTITY> RouteBuilder defaultHandler(
                Class<REQUEST_ENTITY> type, HandlerWithRequestEntity<REQUEST_ENTITY, ?> handler, HandlerConfigBuilder config);
    }

    @FunctionalInterface interface Handler<RESPONSE_ENTITY> {
        RESPONSE_ENTITY handleRequest(RequestContext context) throws RestApiException;
    }

    @FunctionalInterface interface HandlerWithRequestEntity<REQUEST_ENTITY, RESPONSE_ENTITY> {
        RESPONSE_ENTITY handleRequest(RequestContext context, REQUEST_ENTITY requestEntity) throws RestApiException;
    }

    @FunctionalInterface interface ExceptionMapper<EXCEPTION extends RuntimeException> { HttpResponse toResponse(RequestContext context, EXCEPTION exception); }

    @FunctionalInterface interface ResponseMapper<RESPONSE_ENTITY> { HttpResponse toHttpResponse(RequestContext context, RESPONSE_ENTITY responseEntity) throws RestApiException; }

    @FunctionalInterface interface RequestMapper<REQUEST_ENTITY> { Optional<REQUEST_ENTITY> toRequestEntity(RequestContext context) throws RestApiException; }

    @FunctionalInterface interface Filter { HttpResponse filterRequest(FilterContext context); }

    interface HandlerConfigBuilder {
        HandlerConfigBuilder withRequiredCapabilities(Capability... capabilities);
        HandlerConfigBuilder withRequiredCapabilities(CapabilitySet capabilities);
        HandlerConfigBuilder withReadAclAction();
        HandlerConfigBuilder withWriteAclAction();
        HandlerConfigBuilder withCustomAclAction(AclMapping.Action action);
    }

    interface RequestContext {
        HttpRequest request();
        Method method();
        PathParameters pathParameters();
        QueryParameters queryParameters();
        Headers headers();
        Attributes attributes();
        Optional<RequestContent> requestContent();
        RequestContent requestContentOrThrow();
        ObjectMapper jacksonJsonMapper();
        /** Scheme, domain and port, for the original request. <em>Use this only for generating resources links, not for custom routing!</em> */
        // TODO: this needs to include path and query as well, to be useful for generating resource links that need not be rewritten.
        HttpURL baseRequestURL();
        AclMapping.Action aclAction();
        Optional<Principal> userPrincipal();
        Principal userPrincipalOrThrow();
        Optional<SSLSession> sslSession();
        Optional<ConnectionAuthContext> connectionAuthContext();

        interface Parameters {
            Optional<String> getString(String name);
            String getStringOrThrow(String name);
            default Optional<Boolean> getBoolean(String name) { return getString(name).map(Boolean::valueOf);}
            default boolean getBooleanOrThrow(String name) { return Boolean.parseBoolean(getStringOrThrow(name)); }
            default OptionalLong getLong(String name) {
                return getString(name).map(Long::parseLong).map(OptionalLong::of).orElseGet(OptionalLong::empty);
            }
            default long getLongOrThrow(String name) { return Long.parseLong(getStringOrThrow(name)); }
            default OptionalDouble getDouble(String name) {
                return getString(name).map(Double::parseDouble).map(OptionalDouble::of).orElseGet(OptionalDouble::empty);
            }
            default double getDoubleOrThrow(String name) { return Double.parseDouble(getStringOrThrow(name)); }
        }

        interface PathParameters extends Parameters {
            HttpURL.Path getFullPath();
            Optional<HttpURL.Path> getRest();
        }
        interface QueryParameters extends Parameters {
            HttpURL.Query getFullQuery();
            List<String> getStringList(String name);
        }
        interface Headers extends Parameters {}

        interface Attributes {
            Optional<Object> get(String name);
            void set(String name, Object value);
        }

        interface RequestContent {
            String contentType();
            InputStream content();
        }
    }

    interface FilterContext {
        RequestContext requestContext();
        String route();
        HttpResponse executeNext();
    }
}