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;
025
026import javax.ws.rs.core.MediaType;
027import javax.ws.rs.core.Response;
028
029import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
030import org.apache.isis.viewer.restfulobjects.applib.util.Parser;
031import org.jboss.resteasy.client.core.BaseClientResponse;
032
033import com.google.common.collect.Maps;
034
035public final class RestfulRequest {
036
037    public enum DomainModel {
038        NONE, SIMPLE, FORMAL, SELECTABLE;
039
040        public static Parser<DomainModel> parser() {
041            return new Parser<RestfulRequest.DomainModel>() {
042
043                @Override
044                public DomainModel valueOf(final String str) {
045                    return DomainModel.valueOf(str.toUpperCase());
046                }
047
048                @Override
049                public String asString(final DomainModel t) {
050                    return t.name().toLowerCase();
051                }
052            };
053        }
054
055        @Override
056        public String toString() {
057            return name().toLowerCase();
058        }
059    }
060
061    public static class RequestParameter<Q> {
062
063        public static RequestParameter<List<List<String>>> FOLLOW_LINKS = new RequestParameter<List<List<String>>>("x-ro-follow-links", Parser.forListOfListOfStrings(), Collections.<List<String>> emptyList());
064        public static RequestParameter<Integer> PAGE = new RequestParameter<Integer>("x-ro-page", Parser.forInteger(), 1);
065        public static RequestParameter<Integer> PAGE_SIZE = new RequestParameter<Integer>("x-ro-page-size", Parser.forInteger(), 25);
066        public static RequestParameter<List<String>> SORT_BY = new RequestParameter<List<String>>("x-ro-sort-by", Parser.forListOfStrings(), Collections.<String> emptyList());
067        public static RequestParameter<DomainModel> DOMAIN_MODEL = new RequestParameter<DomainModel>("x-ro-domain-model", DomainModel.parser(), DomainModel.FORMAL);
068        public static RequestParameter<Boolean> VALIDATE_ONLY = new RequestParameter<Boolean>("x-ro-validate-only", Parser.forBoolean(), false);
069
070        private final String name;
071        private final Parser<Q> parser;
072        private final Q defaultValue;
073
074        private RequestParameter(final String name, final Parser<Q> parser, final Q defaultValue) {
075            this.name = name;
076            this.parser = parser;
077            this.defaultValue = defaultValue;
078        }
079
080        public String getName() {
081            return name;
082        }
083
084        public Parser<Q> getParser() {
085            return parser;
086        }
087
088        public Q valueOf(final JsonRepresentation parameterRepresentation) {
089            if (parameterRepresentation == null) {
090                return defaultValue;
091            }
092            if (!parameterRepresentation.isMap()) {
093                return defaultValue;
094            }
095            final Q parsedValue = getParser().valueOf(parameterRepresentation.getRepresentation(getName()));
096            return parsedValue != null ? parsedValue : defaultValue;
097        }
098
099        public Q getDefault() {
100            return defaultValue;
101        }
102
103        @Override
104        public String toString() {
105            return getName();
106        }
107    }
108
109    public static class Header<X> {
110        public static Header<String> IF_MATCH = new Header<String>("If-Match", Parser.forString());
111        public static Header<List<MediaType>> ACCEPT = new Header<List<MediaType>>("Accept", Parser.forListOfJaxRsMediaTypes());
112
113        private final String name;
114        private final Parser<X> parser;
115
116        /**
117         * public visibility for testing purposes only.
118         */
119        public Header(final String name, final Parser<X> parser) {
120            this.name = name;
121            this.parser = parser;
122        }
123
124        public String getName() {
125            return name;
126        }
127
128        public Parser<X> getParser() {
129            return parser;
130        }
131
132        void setHeader(final ClientRequestConfigurer clientRequestConfigurer, final X t) {
133            clientRequestConfigurer.header(getName(), parser.asString(t));
134        }
135
136        @Override
137        public String toString() {
138            return getName();
139        }
140    }
141
142    private final ClientRequestConfigurer clientRequestConfigurer;
143    private final Map<RequestParameter<?>, Object> args = Maps.newLinkedHashMap();
144
145    public RestfulRequest(final ClientRequestConfigurer clientRequestConfigurer) {
146        this.clientRequestConfigurer = clientRequestConfigurer;
147    }
148
149    public <T> RestfulRequest withHeader(final Header<T> header, final T t) {
150        header.setHeader(clientRequestConfigurer, t);
151        return this;
152    }
153
154    public <T> RestfulRequest withHeader(final Header<List<T>> header, final T... ts) {
155        header.setHeader(clientRequestConfigurer, Arrays.asList(ts));
156        return this;
157    }
158
159    public <Q> RestfulRequest withArg(final RestfulRequest.RequestParameter<Q> queryParam, final String argStrFormat, final Object... args) {
160        final String argStr = String.format(argStrFormat, args);
161        final Q arg = queryParam.getParser().valueOf(argStr);
162        return withArg(queryParam, arg);
163    }
164
165    public <Q> RestfulRequest withArg(final RestfulRequest.RequestParameter<Q> queryParam, final Q arg) {
166        args.put(queryParam, arg);
167        return this;
168    }
169
170    public RestfulResponse<JsonRepresentation> execute() {
171        try {
172            if (!args.isEmpty()) {
173                clientRequestConfigurer.configureArgs(args);
174            }
175            final Response response = clientRequestConfigurer.getClientRequest().execute();
176
177            // this is a bit hacky
178            @SuppressWarnings("unchecked")
179            final BaseClientResponse<String> restEasyResponse = (BaseClientResponse<String>) response;
180            restEasyResponse.setReturnType(String.class);
181
182            return RestfulResponse.ofT(response);
183        } catch (final Exception ex) {
184            throw new RuntimeException(ex);
185        }
186    }
187
188    @SuppressWarnings("unchecked")
189    public <T extends JsonRepresentation> RestfulResponse<T> executeT() {
190        final RestfulResponse<JsonRepresentation> restfulResponse = execute();
191        return (RestfulResponse<T>) restfulResponse;
192    }
193
194    /**
195     * For testing only.
196     */
197    ClientRequestConfigurer getClientRequestConfigurer() {
198        return clientRequestConfigurer;
199    }
200
201}