001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.restfulEndpoints; 019 020import java.util.*; 021 022import static io.stallion.utils.Literals.*; 023 024import io.stallion.dataAccess.Model; 025import io.stallion.dataAccess.ModelController; 026import io.stallion.dataAccess.filtering.*; 027import io.stallion.requests.IRequest; 028import io.stallion.services.Log; 029import io.stallion.utils.json.JSON; 030import org.apache.commons.lang3.StringUtils; 031 032 033public class QueryToPager<T extends Model> { 034 /* 035 .filter() 036 .exclude() 037 .allSortable() 038 .allFilters() 039 .allowedFilters() 040 .allowedSortable() 041 .searchFields() 042 .pageSize() 043 .pager() 044 .chain(); 045 046 */ 047 private FilterChain<T> chain; 048 private ModelController<T> controller; 049 private IRequest request; 050 private List<String> _allowedFilters = list(); 051 private List<String> _allowedSortable = list(); 052 private List<String> _searchFields = list(); 053 private boolean _allFilters = false; 054 private boolean _allSorts = false; 055 private Integer _pageSize = 50; 056 private boolean _requestProcessed = false; 057 private String defaultSort = ""; 058 059 060 061 062 063 public QueryToPager(IRequest request, ModelController<T> controller) { 064 this(request, controller, controller.filterChain()); 065 } 066 067 public QueryToPager(IRequest request, ModelController<T> controller, FilterChain<T> chain) { 068 this.request = request; 069 this.controller = controller; 070 this.chain = chain; 071 } 072 073 public QueryToPager<T> filter(String name, Object value, FilterOperator op) { 074 this.chain = chain.filterBy(name, value, op); 075 return this; 076 } 077 078 public QueryToPager<T> exclude(String name, Object value, FilterOperator op) { 079 this.chain = chain.excludeBy(name, value, op); 080 return this; 081 } 082 083 public QueryToPager<T> allowedFilters(String ...fields) { 084 this._allowedFilters = asList(fields); 085 return this; 086 } 087 088 public QueryToPager<T> allowedSortable(String...fields) { 089 this._allowedSortable = asList(fields); 090 return this; 091 } 092 093 public QueryToPager<T> searchFields(String...fields) { 094 this._searchFields = asList(fields); 095 return this; 096 } 097 098 099 100 101 102 public QueryToPager<T> andAnyOf(Or...ors) { 103 this.chain = chain.andAnyOf(ors); 104 return this; 105 } 106 107 public QueryToPager<T> allSortable() { 108 this._allSorts= true; 109 return this; 110 } 111 112 public QueryToPager<T> allFilters() { 113 this._allFilters = true; 114 return this; 115 } 116 117 public QueryToPager<T> pageSize(Integer pageSize) { 118 this._pageSize = pageSize; 119 return this; 120 } 121 122 123 public FilterChain<T> chain() { 124 if (!_requestProcessed) { 125 process(); 126 this._requestProcessed = true; 127 } 128 return this.chain; 129 } 130 131 public Pager<T> pager() { 132 if (!_requestProcessed) { 133 process(); 134 this._requestProcessed = true; 135 } 136 Integer page = 1; 137 if (!empty(request.getParameter("page"))) { 138 page = Integer.parseInt(request.getParameter("page")); 139 } 140 141 return this.chain.pager(page, _pageSize); 142 143 } 144 145 protected void process() { 146 // ?filters=&search=&page=&sort=&filter_by... 147 Map<String, String> params = this.request.getQueryParams(); 148 String search = params.getOrDefault("search", null); 149 if (!empty(search)) { 150 if (!empty(_searchFields)) { 151 this.chain = chain.search(search, asArray(_searchFields, String.class)); 152 } else { 153 Log.warn("Search included, but no search fields defined"); 154 } 155 } 156 String filters = params.getOrDefault("filters", null); 157 if (!empty(filters)) { 158 List<LinkedHashMap> filterObjects = JSON.parseList(filters); 159 for (LinkedHashMap<String, Object> o: filterObjects) { 160 String field = o.get("name").toString(); 161 if (_allFilters && !_allowedFilters.contains(field)) { 162 Log.warn("Filter not allowed: " + field); 163 continue; 164 } 165 166 Object value = o.get("value"); 167 if (value instanceof Collection) { 168 value = new ArrayList((Collection) value); 169 } 170 String operation = (String)o.getOrDefault("op", "="); 171 this.chain = chain.filter(field, value, operation); 172 } 173 } 174 String sort = or(params.getOrDefault("sort", null), defaultSort); 175 if (!empty(sort)) { 176 SortDirection dir = SortDirection.ASC; 177 if (sort.startsWith("-")) { 178 sort = sort.substring(1); 179 dir = SortDirection.DESC; 180 } 181 if (_allSorts && !_allowedSortable.contains(sort)) { 182 Log.warn("Sort not allowed: " + sort); 183 } else { 184 this.chain = chain.sortBy(sort, dir); 185 } 186 } 187 188 for(String filter: request.getQueryParamAsList("filter_by")) { 189 if (empty(filter) || !filter.contains(":")) { 190 continue; 191 } 192 String[] parts = StringUtils.split(filter, ":", 3); 193 String key = parts[0]; 194 if (_allFilters && !_allowedFilters.contains(key)) { 195 Log.warn("Filter not allowed: " + key); 196 continue; 197 } 198 String val; 199 String op = "eq"; 200 if (parts.length > 2) { 201 op = parts[1]; 202 val = parts[2]; 203 } else { 204 val = parts[1]; 205 } 206 this.chain = chain.filter(key, val, op); 207 208 } 209 210 211 212 } 213 214 public QueryToPager setDefaultSort(String defaultSort) { 215 this.defaultSort = defaultSort; 216 return this; 217 } 218}