aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/restapi/RestApi.java
blob: 05528bc79e2c92f05bc714ed2a235d389507a079 (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
// 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 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();

    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();
        RestApi build();
    }

    interface RouteBuilder {
        RouteBuilder name(String name);
        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 withReadAclAction();
        HandlerConfigBuilder withWriteAclAction();
        HandlerConfigBuilder withCustomAclAction(AclMapping.Action action);
    }

    interface RequestContext {
        HttpRequest request();
        PathParameters pathParameters();
        QueryParameters queryParameters();
        Headers headers();
        Attributes attributes();
        Optional<RequestContent> requestContent();
        RequestContent requestContentOrThrow();
        ObjectMapper jacksonJsonMapper();
        /**
         * Creates a URI builder pre-initialized with scheme, host and port.
         * Intended for response generation (e.g for interactive REST APIs).
         * DO NOT USE FOR CUSTOM ROUTING.
         */
        UriBuilder uriBuilder();
        AclMapping.Action aclAction();
        Optional<Principal> userPrincipal();
        Principal userPrincipalOrThrow();

        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 {
            Optional<HttpURL.Path> getRest();
        }
        interface QueryParameters extends Parameters {
            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();
    }
}