diff --git a/src/RESTObjects/RESTAPI_ProvObjects.cpp b/src/RESTObjects/RESTAPI_ProvObjects.cpp index 3f76c8f..1f338c2 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.cpp +++ b/src/RESTObjects/RESTAPI_ProvObjects.cpp @@ -309,6 +309,7 @@ namespace OpenWifi::ProvObjects { field_to_json( Obj,"deviceConfiguration",deviceConfiguration); field_to_json( Obj,"rrm",rrm); field_to_json( Obj,"managementPolicy",managementPolicy); + field_to_json( Obj,"state",state); } bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { @@ -326,6 +327,7 @@ namespace OpenWifi::ProvObjects { field_from_json( Obj,"deviceConfiguration",deviceConfiguration); field_from_json( Obj,"rrm",rrm); field_from_json( Obj,"managementPolicy",managementPolicy); + field_from_json( Obj,"state",state); return true; } catch(...) { @@ -333,6 +335,20 @@ namespace OpenWifi::ProvObjects { return false; } + void InventoryTagList::to_json(Poco::JSON::Object &Obj) const { + field_to_json( Obj,"taglist",taglist); + } + + bool InventoryTagList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json( Obj,"taglist",taglist); + return false; + } catch (...) { + + } + return false; + }; + void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { field_to_json( Obj,"name", name); field_to_json( Obj,"description", description); diff --git a/src/RESTObjects/RESTAPI_ProvObjects.h b/src/RESTObjects/RESTAPI_ProvObjects.h index 6ac9b4f..991b8bf 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.h +++ b/src/RESTObjects/RESTAPI_ProvObjects.h @@ -284,12 +284,22 @@ namespace OpenWifi::ProvObjects { std::string deviceConfiguration; std::string rrm; Types::UUID_t managementPolicy; + std::string state; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + typedef std::vector InventoryTagVec; + struct InventoryTagList { + InventoryTagVec taglist; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct Report { uint64_t snapShot=0; Types::CountedMap tenants; diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index e98f241..40de8f6 100644 --- a/src/framework/MicroService.h +++ b/src/framework/MicroService.h @@ -100,7 +100,8 @@ namespace OpenWifi { EXPIRED_TOKEN, RATE_LIMIT_EXCEEDED, BAD_MFA_TRANSACTION, - MFA_FAILURE + MFA_FAILURE, + SECURITY_SERVICE_UNREACHABLE }; class AppServiceRegistry { @@ -238,6 +239,23 @@ namespace OpenWifi::RESTAPI_utils { Obj.set(Field,A); } + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::Counted3DMapSII &M) { + Poco::JSON::Array A; + for(const auto &[OrgName,MonthlyNumberMap]:M) { + Poco::JSON::Object OrgObject; + OrgObject.set("tag",OrgName); + Poco::JSON::Array MonthlyArray; + for(const auto &[Month,Counter]:MonthlyNumberMap) { + Poco::JSON::Object Inner; + Inner.set("value", Month); + Inner.set("counter", Counter); + MonthlyArray.add(Inner); + } + OrgObject.set("index",MonthlyArray); + A.add(OrgObject); + } + } + template void field_to_json(Poco::JSON::Object &Obj, const char *Field, const T &V, @@ -1078,6 +1096,53 @@ namespace OpenWifi { typedef std::map ConfigurationMap_t; + template class FIFO { + public: + explicit FIFO(uint32_t Size) : Size_(Size) { + Buffer_->reserve(Size_); + } + + mutable Poco::BasicEvent Writable_; + mutable Poco::BasicEvent Readable_; + + inline bool Read(T &t) { + { + std::lock_guard M(Mutex_); + if (Write_ == Read_) { + return false; + } + + t = (*Buffer_)[Read_++]; + if (Read_ == Size_) { + Read_ = 0; + } + } + bool flag = true; + Writable_.notify(this, flag); + return true; + } + + inline bool Write(const T &t) { + { + std::lock_guard M(Mutex_); + (*Buffer_)[Write_++] = t; + if (Write_ == Size_) { + Write_ = 0; + } + } + bool flag = true; + Readable_.notify(this, flag); + return false; + } + + private: + std::mutex Mutex_; + uint32_t Size_; + uint32_t Read_=0; + uint32_t Write_=0; + std::unique_ptr> Buffer_=std::make_unique>(); + }; + template class RecordCache { public: explicit RecordCache( KeyType Record::* Q) : @@ -1643,11 +1708,14 @@ namespace OpenWifi { if (!ContinueProcessing()) return; - bool Expired=false; - if (AlwaysAuthorize_ && !IsAuthorized(Expired, SubOnlyService_)) { + bool Expired=false, Contacted=false; + if (AlwaysAuthorize_ && !IsAuthorized(Expired, Contacted, SubOnlyService_)) { if(Expired) return UnAuthorized(RESTAPI::Errors::ExpiredToken, EXPIRED_TOKEN); - return UnAuthorized(RESTAPI::Errors::InvalidCredentials, INVALID_TOKEN); + if(Contacted) + return UnAuthorized(RESTAPI::Errors::InvalidCredentials, INVALID_TOKEN); + else + return UnAuthorized(RESTAPI::Errors::InvalidCredentials, SECURITY_SERVICE_UNREACHABLE); } std::string Reason; @@ -2029,7 +2097,7 @@ namespace OpenWifi { return true; } - inline bool IsAuthorized(bool & Expired, bool SubOnly = false ); + inline bool IsAuthorized(bool & Expired, bool & Contacted, bool SubOnly = false ); inline void ReturnObject(Poco::JSON::Object &Object) { PrepareResponse(); @@ -2105,20 +2173,20 @@ namespace OpenWifi { virtual void DoPost() = 0 ; virtual void DoPut() = 0 ; + Poco::Net::HTTPServerRequest *Request= nullptr; + Poco::Net::HTTPServerResponse *Response= nullptr; + SecurityObjects::UserInfoAndPolicy UserInfo_; protected: BindingMap Bindings_; Poco::URI::QueryParameters Parameters_; Poco::Logger &Logger_; std::string SessionToken_; - SecurityObjects::UserInfoAndPolicy UserInfo_; std::vector Methods_; QueryBlock QB_; bool Internal_=false; bool RateLimited_=false; bool QueryBlockInitialized_=false; bool SubOnlyService_=false; - Poco::Net::HTTPServerRequest *Request= nullptr; - Poco::Net::HTTPServerResponse *Response= nullptr; bool AlwaysAuthorize_=true; Poco::JSON::Parser IncomingParser_; RESTAPI_GenericServer & Server_; @@ -2239,6 +2307,26 @@ namespace OpenWifi { Poco::JSON::Object Body_; }; + class OpenAPIRequestDelete { + public: + explicit OpenAPIRequestDelete( const std::string & Type, + const std::string & EndPoint, + const Types::StringPairVec & QueryData, + uint64_t msTimeout): + Type_(Type), + EndPoint_(EndPoint), + QueryData_(QueryData), + msTimeout_(msTimeout){}; + inline Poco::Net::HTTPServerResponse::HTTPStatus Do(const std::string & BearerToken = ""); + + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + class KafkaProducer : public Poco::Runnable { public: inline void run(); @@ -2424,16 +2512,24 @@ namespace OpenWifi { inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, - bool & Expired, bool Sub=false) { + bool & Expired, bool & Contacted, bool Sub=false) { try { Types::StringPairVec QueryData; QueryData.push_back(std::make_pair("token",SessionToken)); OpenAPIRequestGet Req( uSERVICE_SECURITY, Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", QueryData, - 5000); + 10000); Poco::JSON::Object::Ptr Response; - if(Req.Do(Response)==Poco::Net::HTTPServerResponse::HTTP_OK) { + + auto StatusCode = Req.Do(Response); + if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) { + Contacted = false; + return false; + } + + Contacted = true; + if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) { if(Response->has("tokenInfo") && Response->has("userInfo")) { UInfo.from_json(Response); if(IsTokenExpired(UInfo.webtoken)) { @@ -2444,17 +2540,18 @@ namespace OpenWifi { std::lock_guard G(Mutex_); Cache_.update(SessionToken, UInfo); return true; - } + } else { + return false; + } } } catch (...) { - } Expired = false; return false; } inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, - bool & Expired, bool Sub = false) { + bool & Expired, bool & Contacted, bool Sub = false) { std::lock_guard G(Mutex_); auto User = Cache_.get(SessionToken); if(!User.isNull()) { @@ -2466,7 +2563,7 @@ namespace OpenWifi { UInfo = *User; return true; } - return RetrieveTokenInformation(SessionToken, UInfo, Expired, Sub); + return RetrieveTokenInformation(SessionToken, UInfo, Expired, Contacted, Sub); } private: @@ -3519,11 +3616,51 @@ namespace OpenWifi { KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false); } + inline void KafkaLoggerFun(cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string &messqge) { + switch ((cppkafka::LogLevel) level) { + case cppkafka::LogLevel::LogNotice: { + KafkaManager()->Logger().notice(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + case cppkafka::LogLevel::LogDebug: { + KafkaManager()->Logger().debug(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + case cppkafka::LogLevel::LogInfo: { + KafkaManager()->Logger().information(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + case cppkafka::LogLevel::LogWarning: { + KafkaManager()->Logger().warning(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + case cppkafka::LogLevel::LogAlert: + case cppkafka::LogLevel::LogCrit: { + KafkaManager()->Logger().critical(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + case cppkafka::LogLevel::LogErr: + case cppkafka::LogLevel::LogEmerg: + default: { + KafkaManager()->Logger().error(Poco::format("kafka-log: facility: %s message: %s",facility, messqge)); + } + break; + } + } + + inline void KafkaErrorFun(cppkafka::KafkaHandleBase & handle, int error, const std::string &reason) { + KafkaManager()->Logger().error(Poco::format("kafka-error: %d, reason: %s", error, reason)); + } + inline void KafkaProducer::run() { cppkafka::Configuration Config({ { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } }); + + Config.set_log_callback(KafkaLoggerFun); + Config.set_error_callback(KafkaErrorFun); + KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + std::to_string(MicroService::instance().ID()) + R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() + @@ -3564,6 +3701,9 @@ namespace OpenWifi { { "enable.partition.eof", false } }); + Config.set_log_callback(KafkaLoggerFun); + Config.set_error_callback(KafkaErrorFun); + cppkafka::TopicConfiguration topic_config = { { "auto.offset.reset", "smallest" } }; @@ -3930,6 +4070,52 @@ namespace OpenWifi { return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; } + inline Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestDelete::Do(const std::string & BearerToken) { + try { + auto Services = MicroService::instance().GetServices(Type_); + + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + std::string Path(URI.getPathAndQuery()); + Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); + + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_DELETE, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + std::ostringstream obody; + Poco::JSON::Stringifier::stringify(Body_,obody); + + Request.setContentType("application/json"); + Request.setContentLength(obody.str().size()); + + if(BearerToken.empty()) { + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); + } else { + // Authorization: Bearer ${token} + Request.add("Authorization", "Bearer " + BearerToken); + } + + std::ostream & os = Session.sendRequest(Request); + os << obody.str(); + + Poco::Net::HTTPResponse Response; + Session.receiveResponse(Response); + return Response.getStatus(); + } + } + catch (const Poco::Exception &E) + { + std::cerr << E.displayText() << std::endl; + } + return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; + } inline void RESTAPI_GenericServer::InitLogging() { std::string Public = MicroService::instance().ConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); @@ -3946,7 +4132,7 @@ namespace OpenWifi { #ifdef TIP_SECURITY_SERVICE [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired , bool Sub ); #endif - inline bool RESTAPIHandler::IsAuthorized( bool & Expired , bool Sub ) { + inline bool RESTAPIHandler::IsAuthorized( bool & Expired , bool & Contacted , bool Sub ) { if(Internal_ && Request->has("X-INTERNAL-NAME")) { auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); if(!Allowed) { @@ -3978,7 +4164,7 @@ namespace OpenWifi { #ifdef TIP_SECURITY_SERVICE if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired, Sub)) { #else - if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Sub)) { + if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Contacted, Sub)) { #endif if(Server_.LogIt(Request->getMethod(),true)) { Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s'", diff --git a/src/framework/OpenWifiTypes.h b/src/framework/OpenWifiTypes.h index c5b3804..6e368b0 100644 --- a/src/framework/OpenWifiTypes.h +++ b/src/framework/OpenWifiTypes.h @@ -14,22 +14,24 @@ #include namespace OpenWifi::Types { - typedef std::pair StringPair; - typedef std::vector StringPairVec; - typedef std::queue StringPairQueue; - typedef std::vector StringVec; - typedef std::set StringSet; - typedef std::map> StringMapStringSet; - typedef std::function TopicNotifyFunction; - typedef std::list> TopicNotifyFunctionList; - typedef std::map NotifyTable; - typedef std::map CountedMap; - typedef std::vector TagList; - typedef std::string UUID_t; - typedef std::vector UUIDvec_t; + typedef std::pair StringPair; + typedef std::vector StringPairVec; + typedef std::queue StringPairQueue; + typedef std::vector StringVec; + typedef std::set StringSet; + typedef std::map> StringMapStringSet; + typedef std::function TopicNotifyFunction; + typedef std::list> TopicNotifyFunctionList; + typedef std::map NotifyTable; + typedef std::map CountedMap; + typedef std::vector TagList; + typedef std::string UUID_t; + typedef std::vector UUIDvec_t; + typedef std::map> Counted3DMapSII; } namespace OpenWifi { + inline void UpdateCountedMap(OpenWifi::Types::CountedMap &M, const std::string &S, uint64_t Increment=1) { auto it = M.find(S); if(it==M.end()) @@ -37,4 +39,22 @@ namespace OpenWifi { else it->second += Increment; } + + inline void UpdateCountedMap(OpenWifi::Types::Counted3DMapSII &M, const std::string &S, uint32_t Index, uint64_t Increment=1) { + auto it = M.find(S); + if(it==M.end()) { + std::map E; + E[Index] = Increment; + M[S] = E; + } + else { + std::map & IndexMap = it->second; + auto it_index = IndexMap.find(Index); + if(it_index == IndexMap.end()) { + IndexMap[Index] = Increment; + } else { + it_index->second += Increment; + } + } + } } diff --git a/src/framework/RESTAPI_errors.h b/src/framework/RESTAPI_errors.h index b203797..c35f2fa 100644 --- a/src/framework/RESTAPI_errors.h +++ b/src/framework/RESTAPI_errors.h @@ -14,7 +14,7 @@ namespace OpenWifi::RESTAPI::Errors { static const std::string CouldNotBeDeleted{"Element could not be deleted."}; static const std::string NameMustBeSet{"The name property must be set."}; static const std::string ConfigBlockInvalid{"Configuration block type invalid."}; - static const std::string UnknownId{"Unknown management policy."}; + static const std::string UnknownId{"Unknown UUID."}; static const std::string InvalidDeviceTypes{"Unknown or invalid device type(s)."}; static const std::string RecordNotCreated{"Record could not be created."}; static const std::string RecordNotUpdated{"Record could not be updated."}; @@ -59,5 +59,6 @@ namespace OpenWifi::RESTAPI::Errors { static const std::string MissingAuthenticationInformation{"Missing authentication information."}; static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."}; static const std::string ExpiredToken{"Token has expired, user must login."}; + static const std::string SubscriberMustExist{"Subscriber must exist."}; } diff --git a/src/framework/orm.h b/src/framework/orm.h index 51e9b0b..6b56371 100644 --- a/src/framework/orm.h +++ b/src/framework/orm.h @@ -171,6 +171,9 @@ namespace ORM { template class DB { public: + + typedef const char * field_name_t; + DB( OpenWifi::DBType dbtype, const char *TableName, const FieldVec & Fields, @@ -258,27 +261,27 @@ namespace ORM { [[nodiscard]] const std::string & SelectList() const { return SelectList_; }; [[nodiscard]] const std::string & UpdateFields() const { return UpdateFields_; }; - inline std::string OP(const char *F, SqlComparison O , bool V) { + inline std::string OP(field_name_t F, SqlComparison O , bool V) { assert( FieldNames_.find(F) != FieldNames_.end() ); return std::string{"("} + F + SQLCOMPS[O] + (V ? "true" : "false") + ")" ; } - inline std::string OP(const char *F, SqlComparison O , int V) { + inline std::string OP(field_name_t F, SqlComparison O , int V) { assert( FieldNames_.find(F) != FieldNames_.end() ); return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; } - inline std::string OP(const char *F, SqlComparison O , uint64_t V) { + inline std::string OP(field_name_t F, SqlComparison O , uint64_t V) { assert( FieldNames_.find(F) != FieldNames_.end() ); return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; } - std::string OP(const char *F, SqlComparison O , const std::string & V) { + std::string OP(field_name_t F, SqlComparison O , const std::string & V) { assert( FieldNames_.find(F) != FieldNames_.end() ); return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; } - std::string OP(const char *F, SqlComparison O , const char * V) { + std::string OP(field_name_t F, SqlComparison O , const char * V) { assert( FieldNames_.find(F) != FieldNames_.end() ); return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; } @@ -299,9 +302,12 @@ namespace ORM { return std::string{"("} + P1 + BOPS[BOP] + OP(true, P2, More...); } - inline bool Create() { - std::string S; + bool Upgrade() { + uint32_t To; + return Upgrade(0, To); + } + inline bool Create() { switch(Type_) { case OpenWifi::DBType::mysql: { try { @@ -309,12 +315,10 @@ namespace ORM { std::string Statement = IndexCreation_.empty() ? "create table if not exists " + TableName_ +" ( " + CreateFields_ + " )" : "create table if not exists " + TableName_ +" ( " + CreateFields_ + " ), " + IndexCreation_[0] + " )"; Session << Statement , Poco::Data::Keywords::now; - return true; } catch (const Poco::Exception &E) { Logger_.error("Failure to create MySQL DB resources."); Logger_.log(E); } - return false; } break; @@ -326,7 +330,6 @@ namespace ORM { for(const auto &i:IndexCreation_) { Session << i , Poco::Data::Keywords::now; } - return true; } catch (const Poco::Exception &E) { Logger_.error("Failure to create SQLITE DB resources."); Logger_.log(E); @@ -342,7 +345,6 @@ namespace ORM { for(const auto &i:IndexCreation_) { Session << i , Poco::Data::Keywords::now; } - return true; } catch (const Poco::Exception &E) { Logger_.error("Failure to create POSTGRESQL DB resources."); Logger_.log(E); @@ -350,7 +352,7 @@ namespace ORM { } break; } - return false; + return Upgrade(); } [[nodiscard]] std::string ConvertParams(const std::string & S) const { @@ -400,7 +402,7 @@ namespace ORM { return false; } - template bool GetRecord( const char * FieldName, const T & Value, RecordType & R) { + template bool GetRecord(field_name_t FieldName, const T & Value, RecordType & R) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -438,7 +440,7 @@ namespace ORM { typedef std::vector StringVec; template < typename T, - typename T0, typename T1> bool GR(const char *FieldName, T & R,T0 &V0, T1 &V1) { + typename T0, typename T1> bool GR(field_name_t FieldName, T & R,T0 &V0, T1 &V1) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -495,7 +497,7 @@ namespace ORM { return false; } - template bool UpdateRecord( const char *FieldName, const T & Value, const RecordType & R) { + template bool UpdateRecord(field_name_t FieldName, const T & Value, const RecordType & R) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -522,7 +524,7 @@ namespace ORM { return false; } - template bool ReplaceRecord( const char *FieldName, const T & Value, RecordType & R) { + template bool ReplaceRecord(field_name_t FieldName, const T & Value, RecordType & R) { try { if(Exists(FieldName, Value)) { return UpdateRecord(FieldName,Value,R); @@ -534,7 +536,7 @@ namespace ORM { return false; } - template bool GetNameAndDescription(const char *FieldName, const T & Value, std::string & Name, std::string & Description ) { + template bool GetNameAndDescription(field_name_t FieldName, const T & Value, std::string & Name, std::string & Description ) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); Poco::Data::Session Session = Pool_.get(); @@ -561,7 +563,7 @@ namespace ORM { return false; } - template bool DeleteRecord( const char *FieldName, const T & Value) { + template bool DeleteRecord(field_name_t FieldName, const T & Value) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -599,7 +601,7 @@ namespace ORM { return false; } - bool Exists(const char *FieldName, const std::string & Value) { + bool Exists(field_name_t FieldName, const std::string & Value) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -613,15 +615,15 @@ namespace ORM { return false; } - bool Iterate( std::function F) { + bool Iterate( std::function F, const std::string & WhereClause = "" ) { try { - uint64_t Offset=1; + uint64_t Offset=0; uint64_t Batch=50; bool Done=false; while(!Done) { std::vector Records; - if(GetRecords(Offset,Batch,Records)) { + if(GetRecords(Offset,Batch,Records, WhereClause)) { for(const auto &i:Records) { if(!F(i)) return true; @@ -691,7 +693,7 @@ namespace ORM { return 0; } - template bool ManipulateVectorMember( X T, const char *FieldName, std::string & ParentUUID, std::string & ChildUUID, bool Add) { + template bool ManipulateVectorMember( X T, field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID, bool Add) { try { assert( FieldNames_.find(FieldName) != FieldNames_.end() ); @@ -751,89 +753,89 @@ namespace ORM { return true; } - inline bool AddChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddChild(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteChild(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddLocation(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteLocation(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddContact(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteContact(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddVenue(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteVenue(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddDevice(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteDevice(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddEntity(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DeleteEntity(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool AddUser(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, true); } - inline bool DelUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { + inline bool DelUser(field_name_t FieldName, const std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, false); } - inline bool AddInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { + inline bool AddInUse(field_name_t FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { std::string FakeUUID{ Prefix + ":" + ChildUUID}; return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, true); } - inline bool DeleteInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { + inline bool DeleteInUse(field_name_t FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { std::string FakeUUID{ Prefix + ":" + ChildUUID}; return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, false); } - inline bool DeleteContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { + inline bool DeleteContact(field_name_t FieldName, std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, false); } - inline bool AddContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { + inline bool AddContact(field_name_t FieldName, std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, true); } - inline bool DeleteLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { + inline bool DeleteLocation(field_name_t FieldName, std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, false); } - inline bool AddLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { + inline bool AddLocation(field_name_t FieldName, std::string & ParentUUID, const std::string & ChildUUID) { return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, true); } - inline bool GetInUse(const char *FieldName, std::string & UUID, std::vector & UUIDs ) { + inline bool GetInUse(field_name_t FieldName, const std::string & UUID, std::vector & UUIDs ) { RecordType R; if(GetRecord(FieldName,UUID,R)) { UUIDs = R.inUse;