LCOV - code coverage report
Current view: top level - extra/webserver/webserver/resource - SPWebResourceResolver.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 317 386 82.1 %
Date: 2024-05-12 00:16:13 Functions: 33 34 97.1 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPWebResourceResolver.h"
      24             : #include "SPWebResource.h"
      25             : #include "SPWebRoot.h"
      26             : #include "SPValid.h"
      27             : 
      28             : namespace STAPPLER_VERSIONIZED stappler::web {
      29             : 
      30             : template <typename Result = bool>
      31             : SP_COVERAGE_TRIVIAL
      32             : static auto exitWithResolverError(StringView text, Value &&data = Value()) -> Result {
      33             :         Root::getCurrent()->error("ResourceResolver", text, move(data));
      34             :         return Result(0);
      35             : }
      36             : 
      37        2450 : static Vector<StringView> parsePath(StringView path) {
      38        2450 :         Vector<StringView> pathVec;
      39        2450 :         if (!path.empty() && (path.front() == ':' || path.starts_with("/:"))) {
      40          25 :                 path.split<StringView::Chars<':'>>([&] (const StringView &v) {
      41         100 :                         pathVec.emplace_back(v);
      42         100 :                 });
      43             :         } else {
      44        2425 :                 path.split<StringView::Chars<'/'>>([&] (const StringView &v) {
      45        4650 :                         pathVec.emplace_back(v);
      46        4650 :                 });
      47             :         }
      48             : 
      49        2450 :         while (!pathVec.empty() && pathVec.back().empty()) {
      50           0 :                 pathVec.pop_back();
      51             :         }
      52             : 
      53        2450 :         if (!pathVec.empty()) {
      54        1875 :                 std::reverse(pathVec.begin(), pathVec.end());
      55        1900 :                 while (pathVec.back().empty() || pathVec.back().equals("/")) {
      56          25 :                         pathVec.pop_back();
      57             :                 }
      58             :         }
      59             : 
      60        2450 :         return pathVec;
      61           0 : }
      62             : 
      63         350 : static bool getSelectResource(ResourceResolver *resv, Vector<StringView> &path, bool &isSingleObject) {
      64         350 :         if (path.size() < 2) {
      65           0 :                 return exitWithResolverError("invalid 'select' query");
      66             :         }
      67             : 
      68         350 :         auto field = resv->getScheme()->getField(path.back());
      69         350 :         if (!field || !field->isIndexed()) {
      70           0 :                 return exitWithResolverError("invalid 'select' query");
      71             :         }
      72         350 :         path.pop_back();
      73             : 
      74         350 :         StringView cmpStr(path.back()); path.pop_back();
      75             : 
      76         350 :         auto decComp = stappler::sql::decodeComparation(cmpStr);
      77         350 :         db::Comparation cmp = decComp.first;
      78         350 :         size_t valuesRequired = (decComp.second ? 2 : 1);
      79             : 
      80         350 :         if (cmp == db::Comparation::Invalid) {
      81         250 :                 cmp = db::Comparation::Equal;
      82         250 :                 if (field->hasFlag(db::Flags::Unique) || field->getTransform() == db::Transform::Alias) {
      83         175 :                         isSingleObject = true;
      84             :                 }
      85         250 :                 if (field->getType() == db::Type::Text) {
      86          50 :                         return resv->selectByQuery(db::Query::Select(field->getName(), cmp, cmpStr));
      87         200 :                 } else if (field->getType() == db::Type::Boolean) {
      88          75 :                         if (valid::validateNumber(cmpStr)) {
      89          25 :                                 return resv->selectByQuery(db::Query::Select(field->getName(), cmp, cmpStr.readInteger().get(), 0));
      90          50 :                         } else if (cmpStr == "t" || cmpStr == "true") {
      91          25 :                                 return resv->selectByQuery(db::Query::Select(field->getName(), cmp, Value(true), Value(false)));
      92          25 :                         } else if (cmpStr == "f" || cmpStr == "false") {
      93          25 :                                 return resv->selectByQuery(db::Query::Select(field->getName(), cmp, Value(false), Value(false)));
      94             :                         }
      95         125 :                 } else if (valid::validateNumber(cmpStr) && db::checkIfComparationIsValid(field->getType(), cmp, field->getFlags())) {
      96         125 :                         return resv->selectByQuery(db::Query::Select(field->getName(), cmp, cmpStr.readInteger().get(), 0));
      97             :                 } else {
      98           0 :                         return exitWithResolverError("invalid 'select' query");
      99             :                 }
     100             :         }
     101             : 
     102         100 :         if (path.size() < valuesRequired || !db::checkIfComparationIsValid(field->getType(), cmp, field->getFlags())) {
     103           0 :                 return exitWithResolverError("invalid 'select' query");
     104             :         }
     105             : 
     106         100 :         if (valuesRequired == 1) {
     107          75 :                 StringView value(std::move(path.back())); path.pop_back();
     108          75 :                 if (field->getType() == db::Type::Text) {
     109          25 :                         return resv->selectByQuery(db::Query::Select(field->getName(), cmp, value));
     110          50 :                 } else if (valid::validateNumber(value)) {
     111          50 :                         return resv->selectByQuery(db::Query::Select(field->getName(), cmp, value.readInteger().get(), 0));
     112             :                 } else {
     113           0 :                         return exitWithResolverError("invalid 'select' query");
     114             :                 }
     115             :         }
     116             : 
     117          25 :         if (valuesRequired == 2) {
     118          25 :                 StringView value1(std::move(path.back())); path.pop_back();
     119          25 :                 StringView value2(std::move(path.back())); path.pop_back();
     120          25 :                 if (valid::validateNumber(value1) && valid::validateNumber(value2)) {
     121          25 :                         return resv->selectByQuery(db::Query::Select(field->getName(), cmp, value1.readInteger().get(), value2.readInteger().get()));
     122             :                 }
     123             :         }
     124             : 
     125           0 :         return false;
     126             : }
     127             : 
     128         225 : static bool getSearchResource(ResourceResolver *resv, Vector<StringView> &path, bool &isSingleObject) {
     129         225 :         if (path.size() < 1) {
     130           0 :                 return exitWithResolverError("invalid 'search' query");
     131             :         }
     132             : 
     133         225 :         auto field = resv->getScheme()->getField(path.back());
     134         225 :         if (!field || field->getType() != db::Type::FullTextView) {
     135           0 :                 return exitWithResolverError("invalid 'search' query");
     136             :         }
     137         225 :         path.pop_back();
     138             : 
     139         225 :         return resv->searchByField(field);
     140             : }
     141             : 
     142          50 : static bool getOrderResource(ResourceResolver *resv, Vector<StringView> &path) {
     143          50 :         if (path.size() < 1) {
     144           0 :                 return exitWithResolverError("invalid 'order' query");
     145             :         }
     146             : 
     147          50 :         auto field = resv->getScheme()->getField(path.back());
     148          50 :         if (!field || !field->isIndexed()) {
     149           0 :                 return exitWithResolverError("invalid 'order' query");
     150             :         }
     151          50 :         path.pop_back();
     152             : 
     153          50 :         db::Ordering ord = db::Ordering::Ascending;
     154          50 :         if (!path.empty()) {
     155          50 :                 if (path.back() == "asc" ) {
     156          25 :                         ord = db::Ordering::Ascending;
     157          25 :                         path.pop_back();
     158          25 :                 } else if (path.back() == "desc") {
     159          25 :                         ord = db::Ordering::Descending;
     160          25 :                         path.pop_back();
     161             :                 }
     162             :         }
     163             : 
     164          50 :         if (!path.empty()) {
     165          25 :                 auto &n = path.back();
     166          25 :                 if (valid::validateNumber(n)) {
     167          25 :                         if (resv->order(field->getName(), ord)) {
     168          25 :                                 auto ret = resv->limit(n.readInteger().get());
     169          25 :                                 path.pop_back();
     170          25 :                                 return ret;
     171             :                         } else {
     172           0 :                                 return false;
     173             :                         }
     174             :                 }
     175             :         }
     176             : 
     177          25 :         return resv->order(field->getName(), ord);
     178             : }
     179             : 
     180          50 : static bool getOrderResource(ResourceResolver *resv, Vector<StringView> &path, const StringView &fieldName, db::Ordering ord) {
     181          50 :         auto field = resv->getScheme()->getField(fieldName);
     182          50 :         if (!field || !field->isIndexed()) {
     183           0 :                 return exitWithResolverError("invalid 'order' query");
     184             :         }
     185             : 
     186          50 :         if (!path.empty()) {
     187          50 :                 auto &n = path.back();
     188          50 :                 if (valid::validateNumber(n)) {
     189          50 :                         if (resv->order(field->getName(), ord)) {
     190          50 :                                 auto ret = resv->limit(n.readInteger().get());
     191          50 :                                 path.pop_back();
     192          50 :                                 return ret;
     193             :                         } else {
     194           0 :                                 return false;
     195             :                         }
     196             :                 }
     197             :         }
     198             : 
     199           0 :         return resv->order(field->getName(), ord);
     200             : }
     201             : 
     202         200 : static bool getLimitResource(ResourceResolver *resv, Vector<StringView> &path, bool &isSingleObject) {
     203         200 :         if (path.size() < 1) {
     204           0 :                 return exitWithResolverError("invalid 'limit' query");
     205             :         }
     206             : 
     207         200 :         StringView value(path.back()); path.pop_back();
     208         200 :         if (valid::validateNumber(value)) {
     209         200 :                 auto val = value.readInteger().get();
     210         200 :                 if (val == 1) {
     211         150 :                         isSingleObject = true;
     212             :                 }
     213         200 :                 return resv->limit(val);
     214             :         } else {
     215           0 :                 return exitWithResolverError("invalid 'limit' query");
     216             :         }
     217             : }
     218             : 
     219          50 : static bool getOffsetResource(ResourceResolver *resv, Vector<StringView> &path) {
     220          50 :         if (path.size() < 1) {
     221           0 :                 return exitWithResolverError("invalid 'offset' query");
     222             :         }
     223             : 
     224          50 :         StringView value(std::move(path.back())); path.pop_back();
     225          50 :         if (valid::validateNumber(value)) {
     226          50 :                 return resv->offset(value.readInteger().get());
     227             :         } else {
     228           0 :                 return exitWithResolverError("invalid 'offset' query");
     229             :         }
     230             : }
     231             : 
     232          75 : static bool getFirstResource(ResourceResolver *resv, Vector<StringView> &path, bool &isSingleObject) {
     233          75 :         if (path.size() < 1) {
     234           0 :                 return exitWithResolverError("invalid 'first' query");
     235             :         }
     236             : 
     237          75 :         auto field = resv->getScheme()->getField(path.back());
     238          75 :         if (!field || !field->isIndexed()) {
     239           0 :                 return exitWithResolverError("invalid 'first' query");
     240             :         }
     241          75 :         path.pop_back();
     242             : 
     243          75 :         if (!path.empty()) {
     244          25 :                 if (valid::validateNumber(path.back())) {
     245          25 :                         size_t val = path.back().readInteger().get();
     246          25 :                         path.pop_back();
     247             : 
     248          25 :                         if (val == 1) {
     249           0 :                                 isSingleObject = true;
     250             :                         }
     251          25 :                         return resv->first(field->getName(), val);
     252             :                 }
     253             :         }
     254             : 
     255          50 :         isSingleObject = true;
     256          50 :         return resv->first(field->getName(), 1);
     257             : }
     258             : 
     259          50 : static bool getLastResource(ResourceResolver *resv, Vector<StringView> &path, bool &isSingleObject) {
     260          50 :         if (path.size() < 1) {
     261           0 :                 return exitWithResolverError("invalid 'last' query");
     262             :         }
     263             : 
     264          50 :         auto field = resv->getScheme()->getField(path.back());
     265          50 :         if (!field || !field->isIndexed()) {
     266           0 :                 return exitWithResolverError("invalid 'last' query");
     267             :         }
     268          50 :         path.pop_back();
     269             : 
     270          50 :         if (!path.empty()) {
     271          25 :                 if (valid::validateNumber(path.back())) {
     272          25 :                         size_t val = path.back().readInteger().get();
     273          25 :                         path.pop_back();
     274             : 
     275          25 :                         if (val == 1) {
     276           0 :                                 isSingleObject = true;
     277             :                         }
     278          25 :                         return resv->last(field->getName(), val);
     279             :                 }
     280             :         }
     281             : 
     282          25 :         isSingleObject = true;
     283          25 :         return resv->last(field->getName(), 1);
     284             : }
     285             : 
     286        1875 : static Resource *parseResource(ResourceResolver *resv, Vector<StringView> &path) {
     287        1875 :         bool isSingleObject = false;
     288        4575 :         while (!path.empty()) {
     289        3075 :                 StringView filter(std::move(path.back()));
     290        3075 :                 path.pop_back();
     291             : 
     292        3075 :                 if (!isSingleObject) {
     293        2200 :                         if (filter.starts_with("id")) {
     294        1000 :                                 filter += "id"_len;
     295        1000 :                                 if (uint64_t id = filter.readInteger().get()) {
     296         975 :                                         if (!resv->selectById(id)) {
     297         375 :                                                 return nullptr;
     298             :                                         }
     299         975 :                                         isSingleObject = true;
     300             :                                 } else {
     301          25 :                                         return nullptr;
     302             :                                 }
     303        1200 :                         } else if (filter.starts_with("named-")) {
     304          25 :                                 filter += "named-"_len;
     305          25 :                                 if (valid::validateIdentifier(filter)) {
     306          25 :                                         if (!resv->selectByAlias(filter)) {
     307           0 :                                                 return nullptr;
     308             :                                         }
     309          25 :                                         isSingleObject = true;
     310             :                                 } else {
     311           0 :                                         return nullptr;
     312             :                                 }
     313        1175 :                         } else if (filter == "all") {
     314         125 :                                 if (!resv->getAll()) {
     315           0 :                                         return nullptr;
     316             :                                 }
     317             :                                 // do nothing
     318        1050 :                         } else if (filter == "select") {
     319         350 :                                 if (!getSelectResource(resv, path, isSingleObject)) {
     320           0 :                                         return nullptr;
     321             :                                 }
     322         700 :                         } else if (filter == "search") {
     323         225 :                                 if (!getSearchResource(resv, path, isSingleObject)) {
     324           0 :                                         return nullptr;
     325             :                                 }
     326         475 :                         } else if (filter == "order") {
     327          50 :                                 if (!getOrderResource(resv, path)) {
     328           0 :                                         return nullptr;
     329             :                                 }
     330         425 :                         } else if (filter.size() > 2 && filter.front() == '+') {
     331          25 :                                 if (!getOrderResource(resv, path, filter.sub(1), db::Ordering::Ascending)) {
     332           0 :                                         return nullptr;
     333             :                                 }
     334         400 :                         } else if (filter.size() > 2 && filter.front() == '-') {
     335          25 :                                 if (!getOrderResource(resv, path, filter.sub(1), db::Ordering::Descending)) {
     336           0 :                                         return nullptr;
     337             :                                 }
     338         375 :                         } else if (filter == "limit") {
     339         200 :                                 if (!getLimitResource(resv, path, isSingleObject)) {
     340           0 :                                         return nullptr;
     341             :                                 }
     342         175 :                         } else if (filter == "offset") {
     343          50 :                                 if (!getOffsetResource(resv, path)) {
     344           0 :                                         return nullptr;
     345             :                                 }
     346         125 :                         } else if (filter == "first") {
     347          75 :                                 if (!getFirstResource(resv, path, isSingleObject)) {
     348           0 :                                         return nullptr;
     349             :                                 }
     350          50 :                         } else if (filter == "last") {
     351          50 :                                 if (!getLastResource(resv, path, isSingleObject)) {
     352           0 :                                         return nullptr;
     353             :                                 }
     354             :                         } else {
     355           0 :                                 return exitWithResolverError<Resource *>("Invalid query", Value(filter));
     356             :                         }
     357             :                 } else {
     358         875 :                         if (filter == "offset" && !path.empty() && valid::validateNumber(path.back())) {
     359           0 :                                 if (resv->offset(path.back().readInteger().get())) {
     360           0 :                                         path.pop_back();
     361           0 :                                         continue;
     362             :                                 }
     363             :                         }
     364             : 
     365         875 :                         auto f = resv->getScheme()->getField(filter);
     366         875 :                         if (!f) {
     367           0 :                                 return exitWithResolverError<Resource *>("No such field", Value(filter));
     368             :                         }
     369             : 
     370         875 :                         auto type = f->getType();
     371         875 :                         if (type == db::Type::File || type == db::Type::Image || type == db::Type::Array) {
     372         350 :                                 isSingleObject = true;
     373         350 :                                 if (!resv->getField(filter, f)) {
     374           0 :                                         return nullptr;
     375             :                                 } else {
     376         350 :                                         return resv->getResult();
     377             :                                 }
     378         525 :                         } else if (type == db::Type::Object) {
     379         175 :                                 isSingleObject = true;
     380         175 :                                 if (!resv->getObject(f)) {
     381           0 :                                         return nullptr;
     382             :                                 }
     383         350 :                         } else if (type == db::Type::Set) {
     384         275 :                                 isSingleObject = false;
     385         275 :                                 if (!resv->getSet(f)) {
     386           0 :                                         return nullptr;
     387             :                                 }
     388          75 :                         } else if (type == db::Type::View) {
     389          75 :                                 isSingleObject = false;
     390          75 :                                 if (!resv->getView(f)) {
     391           0 :                                         return nullptr;
     392             :                                 }
     393             :                         } else {
     394           0 :                                 return exitWithResolverError<Resource *>("Invalid query", Value(filter));
     395             :                         }
     396             :                 }
     397             :         }
     398             : 
     399        1500 :         return resv->getResult();
     400             : }
     401             : 
     402        2450 : static Resource *getResolvedResource(ResourceResolver *resv, Vector<StringView> &path) {
     403        2450 :         if (path.empty()) {
     404         575 :                 return resv->getResult();
     405             :         }
     406        1875 :         return parseResource(resv, path);
     407             : }
     408             : 
     409         325 : Resource *Resource::resolve(const db::Transaction &a, const db::Scheme &scheme, const StringView &path) {
     410         325 :         Value tmp;
     411         650 :         return resolve(a, scheme, path, tmp);
     412         325 : }
     413             : 
     414        2450 : Resource *Resource::resolve(const Transaction &a, const Scheme &scheme, const StringView &path, Value & sub) {
     415        2450 :         auto pathVec = parsePath(path);
     416             : 
     417        2450 :         ResourceResolver resolver(a, scheme);
     418        2450 :         if (sub.isDictionary() && !sub.empty()) {
     419         100 :                 for (auto &it : sub.asDict()) {
     420          50 :                         if (auto f = resolver.getScheme()->getField(it.first)) {
     421          50 :                                 if (f->isIndexed()) {
     422          50 :                                         switch (f->getType()) {
     423          25 :                                         case db::Type::Integer:
     424             :                                         case db::Type::Boolean:
     425             :                                         case db::Type::Object:
     426          25 :                                                 resolver.selectByQuery(
     427          50 :                                                                 db::Query::Select(it.first, db::Comparation::Equal, it.second.getInteger(), 0));
     428          25 :                                                 break;
     429          25 :                                         case db::Type::Text:
     430          25 :                                                 resolver.selectByQuery(
     431          50 :                                                                 db::Query::Select(it.first, db::Comparation::Equal, it.second.getString()));
     432          25 :                                                 break;
     433           0 :                                         default:
     434           0 :                                                 break;
     435             :                                         }
     436             :                                 }
     437             :                         }
     438             :                 }
     439        2400 :         } else if (sub.isInteger()) {
     440          25 :                 auto id = sub.getInteger();
     441          25 :                 if (id) {
     442          25 :                         resolver.selectById(id);
     443             :                 }
     444        2375 :         } else if (sub.isString()) {
     445          25 :                 auto & str = sub.getString();
     446          25 :                 if (!str.empty()) {
     447          25 :                         resolver.selectByAlias(str);
     448             :                 }
     449             :         }
     450             : 
     451        4900 :         return getResolvedResource(&resolver, pathVec);
     452        2450 : }
     453             : 
     454           0 : Resource *Resource::resolve(const db::Transaction &a, const db::Scheme &scheme, Vector<StringView> &pathVec) {
     455           0 :         ResourceResolver resolver(a, scheme);
     456           0 :         return getResolvedResource(&resolver, pathVec);
     457           0 : }
     458             : 
     459        2450 : ResourceResolver::ResourceResolver(const db::Transaction &a, const db::Scheme &scheme)
     460        2450 : : _storage(a), _scheme(&scheme), _queries(a.getAdapter().getApplicationInterface(), &scheme) {
     461        2450 :         _type = Objects;
     462        2450 : }
     463             : 
     464        1000 : bool ResourceResolver::selectById(uint64_t oid) {
     465        1000 :         if (_type == Objects) {
     466        1000 :                 if (_queries.selectById(_scheme, oid)) {
     467        1000 :                         return true;
     468             :                 }
     469             :         }
     470           0 :         return exitWithResolverError("Invalid 'select by id', invalid resource type");
     471             : }
     472             : 
     473          50 : bool ResourceResolver::selectByAlias(const StringView &str) {
     474          50 :         if (_type == Objects) {
     475          50 :                 if (_queries.selectByName(_scheme, str)) {
     476          50 :                         return true;
     477             :                 }
     478             :         }
     479           0 :         return exitWithResolverError("Invalid 'select by alias', invalid resource type");
     480             : }
     481             : 
     482         400 : bool ResourceResolver::selectByQuery(db::Query::Select &&q) {
     483         400 :         if (_type == Objects) {
     484         400 :                 if (_queries.selectByQuery(_scheme, move(q))) {
     485         400 :                         return true;
     486             :                 }
     487             :         }
     488           0 :         return exitWithResolverError("Invalid 'select by query', invalid resource type");
     489             : }
     490             : 
     491         225 : bool ResourceResolver::searchByField(const db::Field *field) {
     492         225 :         if (_type == Objects) {
     493         225 :                 _resource = makeResource(ResourceType::Search, move(_queries), field);
     494         225 :                 _type = Search;
     495         225 :                 return true;
     496             :         }
     497           0 :         return exitWithResolverError("Invalid 'search', invalid resource type");
     498             : }
     499             : 
     500         100 : bool ResourceResolver::order(const StringView &f, db::Ordering o) {
     501         100 :         if (_type == Objects) {
     502         100 :                 if (_queries.order(_scheme, f, o)) {
     503         100 :                         return true;
     504             :                 }
     505             :         }
     506           0 :         return exitWithResolverError("Invalid 'order', invalid resource type");
     507             : }
     508             : 
     509          75 : bool ResourceResolver::first(const StringView &f, size_t v) {
     510          75 :         if (_type == Objects) {
     511          75 :                 if (_queries.first(_scheme, f, v)) {
     512          75 :                         return true;
     513             :                 }
     514             :         }
     515           0 :         return exitWithResolverError("Invalid 'first', invalid resource type");
     516             : }
     517             : 
     518          50 : bool ResourceResolver::last(const StringView &f, size_t v) {
     519          50 :         if (_type == Objects) {
     520          50 :                 if (_queries.last(_scheme, f, v)) {
     521          50 :                         return true;
     522             :                 }
     523             :         }
     524           0 :         return exitWithResolverError("Invalid 'last', invalid resource type");
     525             : }
     526             : 
     527         275 : bool ResourceResolver::limit(size_t limit) {
     528         275 :         if (_type == Objects) {
     529         275 :                 if (_queries.limit(_scheme, limit)) {
     530         275 :                         return true;
     531             :                 }
     532             :         }
     533           0 :         return exitWithResolverError("Invalid 'limit', invalid resource type");
     534             : }
     535             : 
     536          50 : bool ResourceResolver::offset(size_t offset) {
     537          50 :         if (_type == Objects) {
     538          50 :                 if (_queries.offset(_scheme, offset)) {
     539          50 :                         return true;
     540             :                 }
     541             :         }
     542           0 :         return exitWithResolverError("Invalid 'offset', invalid resource type");
     543             : }
     544             : 
     545         175 : bool ResourceResolver::getObject(const db::Field *f) {
     546         175 :         if (_type == Objects) {
     547         175 :                 if (auto fo = static_cast<const db::FieldObject *>(f->getSlot())) {
     548         175 :                         if (_queries.setField(fo->scheme, f)) {
     549         175 :                                 _scheme = fo->scheme;
     550         175 :                                 return true;
     551             :                         }
     552             :                 }
     553             :         }
     554           0 :         return exitWithResolverError("Invalid 'getObject', invalid resource type");
     555             : }
     556             : 
     557         275 : bool ResourceResolver::getSet(const db::Field *f) {
     558         275 :         if (_type == Objects) {
     559         275 :                 if (auto fo = static_cast<const db::FieldObject *>(f->getSlot())) {
     560         275 :                         if (_queries.setField(fo->scheme, f)) {
     561         275 :                                 _scheme = fo->scheme;
     562         275 :                                 return true;
     563             :                         }
     564             :                 }
     565             :         }
     566           0 :         return exitWithResolverError("Invalid 'getSet', invalid resource type");
     567             : }
     568             : 
     569          75 : bool ResourceResolver::getView(const db::Field *f) {
     570          75 :         if (_type == Objects) {
     571          75 :                 if (auto fo = static_cast<const db::FieldView *>(f->getSlot())) {
     572          75 :                         if (_queries.setField(fo->scheme, f)) {
     573          75 :                                 _scheme = fo->scheme;
     574          75 :                                 return true;
     575             :                         }
     576             :                 }
     577             :         }
     578           0 :         return exitWithResolverError("Invalid 'getView', invalid resource type");
     579             : }
     580             : 
     581         350 : bool ResourceResolver::getField(const StringView &str, const db::Field *f) {
     582         350 :         if ((_type == Objects) && f->getType() == db::Type::Array) {
     583         200 :                 _resource = makeResource(ResourceType::Array, move(_queries), f);
     584         200 :                 _type = Array;
     585         200 :                 return true;
     586             :         }
     587             : 
     588         150 :         if (_type == Objects && (f->getType() == db::Type::File || f->getType() == db::Type::Image)) {
     589         150 :                 _resource = makeResource(ResourceType::File, move(_queries), f);
     590         150 :                 _type = File;
     591         150 :                 return true;
     592             :         }
     593           0 :         return exitWithResolverError("Invalid 'getField', invalid resource type");
     594             : }
     595             : 
     596         125 : bool ResourceResolver::getAll() {
     597         125 :         if (_type == Objects) {
     598         125 :                 if (_queries.setAll()) {
     599         125 :                         return true;
     600             :                 }
     601             :         }
     602           0 :         return exitWithResolverError("Invalid 'getAll', invalid resource type or already enabled");
     603             : }
     604             : 
     605        2425 : Resource *ResourceResolver::getResult() {
     606        2425 :         if (_type == Objects) {
     607        1850 :                 if (_queries.empty()) {
     608         725 :                         return makeResource(ResourceType::ResourceList, move(_queries), nullptr);
     609        1125 :                 } else if (_queries.isView()) {
     610          75 :                         return makeResource(ResourceType::View, move(_queries), nullptr);
     611        1050 :                 } else if (_queries.isRefSet()) {
     612         325 :                         if (auto f = _queries.getField()) {
     613         325 :                                 if (f->getType() == db::Type::Object) {
     614         150 :                                         return makeResource(ResourceType::ObjectField, move(_queries), nullptr);
     615             :                                 }
     616             :                         }
     617         175 :                         return makeResource(ResourceType::ReferenceSet, move(_queries), nullptr);
     618         725 :                 } else if (_queries.isObject()) {
     619         375 :                         return makeResource(ResourceType::Object, move(_queries), nullptr);
     620             :                 } else {
     621         350 :                         return makeResource(ResourceType::Set, move(_queries), nullptr);
     622             :                 }
     623             :         }
     624         575 :         return _resource;
     625             : }
     626             : 
     627        1725 : const db::Scheme *ResourceResolver::getScheme() const {
     628        1725 :         return _scheme;
     629             : }
     630             : 
     631        2425 : Resource *ResourceResolver::makeResource(ResourceType type, db::QueryList &&list, const db::Field *f) {
     632        2425 :         switch (type) {
     633         725 :         case ResourceType::ResourceList: return new ResourceReslist(_storage, std::move(list));  break;
     634         175 :         case ResourceType::ReferenceSet: return new ResourceRefSet(_storage, std::move(list)); break;
     635         150 :         case ResourceType::ObjectField: return new ResourceFieldObject(_storage, std::move(list)); break;
     636         375 :         case ResourceType::Object: return new ResourceObject(_storage, std::move(list)); break;
     637         350 :         case ResourceType::Set: return new ResourceSet(_storage, std::move(list)); break;
     638          75 :         case ResourceType::View: return new ResourceView(_storage, std::move(list)); break;
     639         150 :         case ResourceType::File: return new ResourceFile(_storage, std::move(list), f); break;
     640         200 :         case ResourceType::Array: return new ResourceArray(_storage, std::move(list), f); break;
     641         225 :         case ResourceType::Search: return new ResourceSearch(_storage, std::move(list), f); break;
     642             :         }
     643           0 :         return nullptr;
     644             : }
     645             : 
     646             : }

Generated by: LCOV version 1.14