001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.viewer.restfulobjects.applib.client;
020
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.List;
024import java.util.Map;
025import javax.ws.rs.core.MediaType;
026import javax.ws.rs.core.Response;
027import com.google.common.collect.Maps;
028import org.jboss.resteasy.client.ClientRequest;
029import org.jboss.resteasy.client.core.BaseClientResponse;
030import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
031import org.apache.isis.viewer.restfulobjects.applib.util.Parser;
032
033public final class RestfulRequest {
034
035    public enum DomainModel {
036        NONE, SIMPLE, FORMAL, SELECTABLE;
037
038        public static Parser<DomainModel> parser() {
039            return new Parser<RestfulRequest.DomainModel>() {
040
041                @Override
042                public DomainModel valueOf(final String str) {
043                    return DomainModel.valueOf(str.toUpperCase());
044                }
045
046                @Override
047                public String asString(final DomainModel t) {
048                    return t.name().toLowerCase();
049                }
050            };
051        }
052
053        @Override
054        public String toString() {
055            return name().toLowerCase();
056        }
057    }
058
059    public static class RequestParameter<Q> {
060
061        public static RequestParameter<List<List<String>>> FOLLOW_LINKS = new RequestParameter<List<List<String>>>("x-ro-follow-links", Parser.forListOfListOfStrings(), Collections.<List<String>> emptyList());
062        public static RequestParameter<Integer> PAGE = new RequestParameter<Integer>("x-ro-page", Parser.forInteger(), 1);
063        public static RequestParameter<Integer> PAGE_SIZE = new RequestParameter<Integer>("x-ro-page-size", Parser.forInteger(), 25);
064        public static RequestParameter<List<String>> SORT_BY = new RequestParameter<List<String>>("x-ro-sort-by", Parser.forListOfStrings(), Collections.<String> emptyList());
065        public static RequestParameter<DomainModel> DOMAIN_MODEL = new RequestParameter<DomainModel>("x-ro-domain-model", DomainModel.parser(), DomainModel.FORMAL);
066        public static RequestParameter<Boolean> VALIDATE_ONLY = new RequestParameter<Boolean>("x-ro-validate-only", Parser.forBoolean(), false);
067
068        private final String name;
069        private final Parser<Q> parser;
070        private final Q defaultValue;
071
072        private RequestParameter(final String name, final Parser<Q> parser, final Q defaultValue) {
073            this.name = name;
074            this.parser = parser;
075            this.defaultValue = defaultValue;
076        }
077
078        public String getName() {
079            return name;
080        }
081
082        public Parser<Q> getParser() {
083            return parser;
084        }
085
086        public Q valueOf(final JsonRepresentation parameterRepresentation) {
087            if (parameterRepresentation == null) {
088                return defaultValue;
089            }
090            if (!parameterRepresentation.isMap()) {
091                return defaultValue;
092            }
093            final Q parsedValue = getParser().valueOf(parameterRepresentation.getRepresentation(getName()));
094            return parsedValue != null ? parsedValue : defaultValue;
095        }
096
097        public Q getDefault() {
098            return defaultValue;
099        }
100
101        @Override
102        public String toString() {
103            return getName();
104        }
105    }
106
107    public static class Header<X> {
108        public static Header<String> IF_MATCH = new Header<String>("If-Match", Parser.forString());
109        public static Header<List<MediaType>> ACCEPT = new Header<List<MediaType>>("Accept", Parser.forListOfJaxRsMediaTypes());
110
111        private final String name;
112        private final Parser<X> parser;
113
114        /**
115         * public visibility for testing purposes only.
116         */
117        public Header(final String name, final Parser<X> parser) {
118            this.name = name;
119            this.parser = parser;
120        }
121
122        public String getName() {
123            return name;
124        }
125
126        public Parser<X> getParser() {
127            return parser;
128        }
129
130        void setHeader(final ClientRequestConfigurer clientRequestConfigurer, final X t) {
131            clientRequestConfigurer.header(getName(), parser.asString(t));
132        }
133
134        @Override
135        public String toString() {
136            return getName();
137        }
138    }
139
140    private final ClientRequestConfigurer clientRequestConfigurer;
141    private final Map<RequestParameter<?>, Object> args = Maps.newLinkedHashMap();
142
143    public RestfulRequest(final ClientRequestConfigurer clientRequestConfigurer) {
144        this.clientRequestConfigurer = clientRequestConfigurer;
145    }
146
147    public <T> RestfulRequest withHeader(final Header<T> header, final T t) {
148        header.setHeader(clientRequestConfigurer, t);
149        return this;
150    }
151
152    public <T> RestfulRequest withHeader(final Header<List<T>> header, final T... ts) {
153        header.setHeader(clientRequestConfigurer, Arrays.asList(ts));
154        return this;
155    }
156
157    public <Q> RestfulRequest withArg(final RestfulRequest.RequestParameter<Q> queryParam, final String argStrFormat, final Object... args) {
158        final String argStr = String.format(argStrFormat, args);
159        final Q arg = queryParam.getParser().valueOf(argStr);
160        return withArg(queryParam, arg);
161    }
162
163    public <Q> RestfulRequest withArg(final RestfulRequest.RequestParameter<Q> queryParam, final Q arg) {
164        args.put(queryParam, arg);
165        return this;
166    }
167
168    public RestfulResponse<JsonRepresentation> execute() {
169        try {
170            if (!args.isEmpty()) {
171                clientRequestConfigurer.configureArgs(args);
172            }
173            final ClientRequest clientRequest = clientRequestConfigurer.getClientRequest();
174            final Response response = clientRequest.execute();
175
176            // this is a bit hacky
177            @SuppressWarnings("unchecked")
178            final BaseClientResponse<String> restEasyResponse = (BaseClientResponse<String>) response;
179            restEasyResponse.setReturnType(String.class);
180
181            return RestfulResponse.ofT(response);
182        } catch (final Exception ex) {
183            throw new RuntimeException(ex);
184        }
185    }
186
187    @SuppressWarnings("unchecked")
188    public <T extends JsonRepresentation> RestfulResponse<T> executeT() {
189        final RestfulResponse<JsonRepresentation> restfulResponse = execute();
190        return (RestfulResponse<T>) restfulResponse;
191    }
192
193    /**
194     * For testing only.
195     */
196    ClientRequestConfigurer getClientRequestConfigurer() {
197        return clientRequestConfigurer;
198    }
199
200}