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     */
019    package org.apache.isis.viewer.restfulobjects.applib;
020    
021    import java.util.Arrays;
022    import java.util.Collections;
023    import java.util.List;
024    import java.util.Map;
025    
026    import javax.ws.rs.core.MediaType;
027    import javax.ws.rs.core.Response;
028    
029    import com.google.common.collect.Maps;
030    
031    import org.jboss.resteasy.client.core.BaseClientResponse;
032    
033    import org.apache.isis.viewer.restfulobjects.applib.util.Parser;
034    
035    public 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.SIMPLE);
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.forListOfMediaTypes());
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    }