From ec1c32942c311c221520d31dcb92803ab769294f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Jun 2016 22:47:55 +0100 Subject: Parameters can be optional --- icespider/common/routes.ice | 1 + icespider/compile/routeCompiler.cpp | 10 ++++++++-- icespider/core/ihttpRequest.cpp | 30 ++++++++++++++++++++---------- icespider/core/ihttpRequest.h | 15 ++++++++------- icespider/core/irouteHandler.h | 7 +++++++ icespider/core/util.h | 16 ++++++++++++++++ icespider/unittests/test-api.ice | 2 +- icespider/unittests/testApp.cpp | 11 ++++++----- icespider/unittests/testRoutes.json | 3 ++- 9 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 icespider/core/util.h diff --git a/icespider/common/routes.ice b/icespider/common/routes.ice index 8f7b520..01bed51 100644 --- a/icespider/common/routes.ice +++ b/icespider/common/routes.ice @@ -9,6 +9,7 @@ module UserIceSpider { string name; ParameterSource source; optional(0) string key; + bool isOptional = false; }; sequence Parameters; class Route { diff --git a/icespider/compile/routeCompiler.cpp b/icespider/compile/routeCompiler.cpp index aee3f7f..9addd56 100644 --- a/icespider/compile/routeCompiler.cpp +++ b/icespider/compile/routeCompiler.cpp @@ -86,7 +86,7 @@ namespace IceSpider { if (!d->key) d->key = d->name; continue; } - r->params.push_back(new UserIceSpider::Parameter(p->name(), UserIceSpider::ParameterSource::URL, p->name())); + r->params.push_back(new UserIceSpider::Parameter(p->name(), UserIceSpider::ParameterSource::URL, p->name(), false)); } } } @@ -222,8 +222,14 @@ namespace IceSpider { auto o = findOperation(r, units); for (const auto & p : r->params) { auto ip = *std::find_if(o->parameters().begin(), o->parameters().end(), [p](const auto & ip) { return ip->name() == p->name; }); - fprintbf(4, output, "auto _p_%s(request->get%sParam<%s>(_pn_%s));\n", + fprintbf(4, output, "auto _p_%s(request->get%sParam<%s>(_pn_%s)", p->name, getEnumString(p->source), Slice::typeToString(ip->type()), p->name); + if (!p->isOptional) { + fprintbf(0, output, " /\n"); + fprintbf(5, output, " [this]() { return requiredParameterNotFound<%s>(\"%s\", _pn_%s); }", + Slice::typeToString(ip->type()), getEnumString(p->source), p->name); + } + fprintbf(0, output, ");\n"); } fprintbf(4, output, "auto prx = getProxy<%s>(request);\n", proxyName); if (o->returnsData()) { diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp index de26a1c..73367ca 100644 --- a/icespider/core/ihttpRequest.cpp +++ b/icespider/core/ihttpRequest.cpp @@ -1,4 +1,5 @@ #include "ihttpRequest.h" +#include "util.h" #include namespace IceSpider { @@ -17,7 +18,9 @@ namespace IceSpider { IHttpRequest::getDeserializer() const { return Slicer::StreamDeserializerFactory::createNew( - getHeaderParam("Content-Type"), getInputStream()); + getHeaderParam("Content-Type") / []() -> std::string { + throw std::runtime_error("Content-Type must be specified to deserialize payload"); + }, getInputStream()); } Slicer::SerializerPtr @@ -27,18 +30,25 @@ namespace IceSpider { "application/json", getOutputStream()); } + template + inline IceUtil::Optional optionalLexicalCast(const IceUtil::Optional & p) + { + if (p) return boost::lexical_cast(*p); + return IceUtil::Optional(); + } + #define getParams(T) \ - template<> T IHttpRequest::getURLParam(const std::string & key) const { \ - return boost::lexical_cast(getURLParam(key)); } \ - template<> T IHttpRequest::getQueryStringParam(const std::string & key) const { \ - return boost::lexical_cast(getQueryStringParam(key)); } \ - template<> T IHttpRequest::getHeaderParam(const std::string & key) const { \ - return boost::lexical_cast(getHeaderParam(key)); } - template<> std::string IHttpRequest::getURLParam(const std::string & key) const { \ + template<> IceUtil::Optional IHttpRequest::getURLParam(const std::string & key) const { \ + return optionalLexicalCast(getURLParam(key)); } \ + template<> IceUtil::Optional IHttpRequest::getQueryStringParam(const std::string & key) const { \ + return optionalLexicalCast(getQueryStringParam(key)); } \ + template<> IceUtil::Optional IHttpRequest::getHeaderParam(const std::string & key) const { \ + return optionalLexicalCast(getHeaderParam(key)); } + template<> IceUtil::Optional IHttpRequest::getURLParam(const std::string & key) const { \ return getURLParam(key); } - template<> std::string IHttpRequest::getQueryStringParam(const std::string & key) const { \ + template<> IceUtil::Optional IHttpRequest::getQueryStringParam(const std::string & key) const { \ return getQueryStringParam(key); } - template<> std::string IHttpRequest::getHeaderParam(const std::string & key) const { \ + template<> IceUtil::Optional IHttpRequest::getHeaderParam(const std::string & key) const { \ return getHeaderParam(key); } getParams(bool); diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h index 6b1a3b0..da903ef 100644 --- a/icespider/core/ihttpRequest.h +++ b/icespider/core/ihttpRequest.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace IceSpider { class Core; @@ -19,25 +20,25 @@ namespace IceSpider { virtual std::string getRequestPath() const = 0; virtual UserIceSpider::HttpMethod getRequestMethod() const = 0; - virtual std::string getURLParam(const std::string &) const = 0; - virtual std::string getQueryStringParam(const std::string &) const = 0; - virtual std::string getHeaderParam(const std::string &) const = 0; + virtual IceUtil::Optional getURLParam(const std::string &) const = 0; + virtual IceUtil::Optional getQueryStringParam(const std::string &) const = 0; + virtual IceUtil::Optional getHeaderParam(const std::string &) const = 0; virtual Slicer::DeserializerPtr getDeserializer() const; virtual Slicer::SerializerPtr getSerializer() const; virtual std::istream & getInputStream() const = 0; virtual std::ostream & getOutputStream() const = 0; template - T getURLParam(const std::string & key) const; + IceUtil::Optional getURLParam(const std::string & key) const; template - T getBodyParam(const std::string &) const + IceUtil::Optional getBodyParam(const std::string &) const { return Slicer::DeserializeAnyWith(getDeserializer()); } template - T getQueryStringParam(const std::string & key) const; + IceUtil::Optional getQueryStringParam(const std::string & key) const; template - T getHeaderParam(const std::string & key) const; + IceUtil::Optional getHeaderParam(const std::string & key) const; template void response(const T & t) const { diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h index 416048e..911c1b4 100644 --- a/icespider/core/irouteHandler.h +++ b/icespider/core/irouteHandler.h @@ -2,6 +2,7 @@ #define ICESPIDER_IROUTEHANDLER_H #include "ihttpRequest.h" +#include "util.h" #include "paths.h" #include #include @@ -16,6 +17,12 @@ namespace IceSpider { const UserIceSpider::HttpMethod method; protected: + template + inline T requiredParameterNotFound(const char *, const std::string & key) const + { + throw std::runtime_error("Required parameter not found: " + key); + } + Ice::ObjectPrx getProxy(IHttpRequest *, const char *) const; template diff --git a/icespider/core/util.h b/icespider/core/util.h new file mode 100644 index 0000000..638660c --- /dev/null +++ b/icespider/core/util.h @@ -0,0 +1,16 @@ +#ifndef ICESPIDER_CORE_UTIL_H +#define ICESPIDER_CORE_UTIL_H + +#include + +namespace IceUtil { + template + T operator/(const IceUtil::Optional & o, const TF & tf) + { + if (o) return *o; + return tf(); + } +} + +#endif + diff --git a/icespider/unittests/test-api.ice b/icespider/unittests/test-api.ice index 4fc87bd..ff43dc2 100644 --- a/icespider/unittests/test-api.ice +++ b/icespider/unittests/test-api.ice @@ -7,7 +7,7 @@ module TestIceSpider { SomeModel index(); SomeModel withParams(string s, int i); void returnNothing(string s); - void complexParam(string s, SomeModel m); + void complexParam(optional(0) string s, SomeModel m); }; }; diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp index aa00c43..e91f618 100644 --- a/icespider/unittests/testApp.cpp +++ b/icespider/unittests/testApp.cpp @@ -55,17 +55,17 @@ class TestRequest : public IceSpider::IHttpRequest { return method; } - std::string getURLParam(const std::string & key) const override + IceUtil::Optional getURLParam(const std::string & key) const override { return AdHoc::safeMapLookup(url, key); } - std::string getQueryStringParam(const std::string & key) const override + IceUtil::Optional getQueryStringParam(const std::string & key) const override { return AdHoc::safeMapLookup(qs, key); } - std::string getHeaderParam(const std::string & key) const override + IceUtil::Optional getHeaderParam(const std::string & key) const override { return AdHoc::safeMapLookup(hdr, key); } @@ -140,9 +140,10 @@ class TestSerice : public TestIceSpider::TestApi { BOOST_REQUIRE_EQUAL(s, "some value"); } - void complexParam(const std::string & s, const TestIceSpider::SomeModelPtr & m, const Ice::Current &) override + void complexParam(const IceUtil::Optional & s, const TestIceSpider::SomeModelPtr & m, const Ice::Current &) override { - BOOST_REQUIRE_EQUAL(s, "1234"); + BOOST_REQUIRE(s); + BOOST_REQUIRE_EQUAL("1234", *s); BOOST_REQUIRE(m); BOOST_REQUIRE_EQUAL("some value", m->value); } diff --git a/icespider/unittests/testRoutes.json b/icespider/unittests/testRoutes.json index 2b7b48d..b766f31 100644 --- a/icespider/unittests/testRoutes.json +++ b/icespider/unittests/testRoutes.json @@ -28,7 +28,8 @@ { "name": "s", "source": "URL", - "key": "id" + "key": "id", + "isOptional": true }, { "name": "m", -- cgit v1.2.3