From 938b3cda6d917c52e264b0f68078ca678cbec27b Mon Sep 17 00:00:00 2001 From: =?utf8?q?C=C3=A9dric=20Aguerre?= Date: Thu, 27 Aug 2015 15:48:07 +0200 Subject: [PATCH] add StudyService --- projects/GDE_API_CPP/api/src/CMakeLists.txt | 3 + projects/GDE_API_CPP/api/src/GDESession.cpp | 29 +++++ projects/GDE_API_CPP/api/src/GDESession.hpp | 13 ++- .../GDE_API_CPP/api/src/JsonFormatter.cpp | 24 +++- .../GDE_API_CPP/api/src/JsonFormatter.hpp | 7 +- projects/GDE_API_CPP/api/src/Study.hpp | 84 +++++++++++++ projects/GDE_API_CPP/api/src/StudyService.cpp | 110 ++++++++++++++++++ projects/GDE_API_CPP/api/src/StudyService.hpp | 35 ++++++ projects/GDE_API_CPP/api/tests/StudyTest.cpp | 55 +++++++++ projects/GDE_API_CPP/api/tests/StudyTest.hpp | 22 ++++ .../GDE_API_CPP/api/tests/TestUtilities.hpp | 4 +- .../com/edf/gde/services/BaseService.java | 12 +- 12 files changed, 386 insertions(+), 12 deletions(-) create mode 100644 projects/GDE_API_CPP/api/src/Study.hpp create mode 100644 projects/GDE_API_CPP/api/src/StudyService.cpp create mode 100644 projects/GDE_API_CPP/api/src/StudyService.hpp create mode 100644 projects/GDE_API_CPP/api/tests/StudyTest.cpp create mode 100644 projects/GDE_API_CPP/api/tests/StudyTest.hpp diff --git a/projects/GDE_API_CPP/api/src/CMakeLists.txt b/projects/GDE_API_CPP/api/src/CMakeLists.txt index 10d44a4..477a3e4 100644 --- a/projects/GDE_API_CPP/api/src/CMakeLists.txt +++ b/projects/GDE_API_CPP/api/src/CMakeLists.txt @@ -22,6 +22,8 @@ SET(gde_api_cpp_HEADERS Group.hpp HttpConnection.hpp JsonFormatter.hpp + Study.hpp + StudyService.hpp User.hpp UserService.hpp ) @@ -33,6 +35,7 @@ SET(gde_api_cpp_SOURCES GDESession.cpp HttpConnection.cpp JsonFormatter.cpp + StudyService.cpp UserService.cpp ) diff --git a/projects/GDE_API_CPP/api/src/GDESession.cpp b/projects/GDE_API_CPP/api/src/GDESession.cpp index 81956e8..eadcb52 100644 --- a/projects/GDE_API_CPP/api/src/GDESession.cpp +++ b/projects/GDE_API_CPP/api/src/GDESession.cpp @@ -1,5 +1,6 @@ #include "GDESession.hpp" #include "UserService.hpp" +#include "StudyService.hpp" std::string gde::GDESession::getServiceURI(const std::string& serviceName) const @@ -16,6 +17,8 @@ gde::GDESession::getServiceURI(const std::string& serviceName) const return uri; } +/* UserService */ + const gde::User gde::GDESession::createUser(const std::string& name, const std::string& password) { @@ -63,3 +66,29 @@ gde::GDESession::removeFromGroup(const gde::Group& group, const gde::User& user) { return gde::UserService(*this).removeFromGroup(group, user); } + +/* StudyService */ + +const gde::Study +gde::GDESession::createStudy(const std::string& name) +{ + return gde::StudyService(*this).createStudy(name); +} + +bool +gde::GDESession::deleteStudy(const gde::Study& study) +{ + return gde::StudyService(*this).deleteStudy(study); +} + +bool +gde::GDESession::setStudyState(const gde::Study& study, int lock) +{ + return gde::StudyService(*this).setStudyState(study, lock); +} + +const gde::Study +gde::GDESession::readStudy(int studyId) +{ + return gde::StudyService(*this).readStudy(studyId); +} diff --git a/projects/GDE_API_CPP/api/src/GDESession.hpp b/projects/GDE_API_CPP/api/src/GDESession.hpp index e07efea..c8abbb2 100644 --- a/projects/GDE_API_CPP/api/src/GDESession.hpp +++ b/projects/GDE_API_CPP/api/src/GDESession.hpp @@ -1,9 +1,10 @@ #ifndef GDE_SESSION_HPP #define GDE_SESSION_HPP +#include "Credentials.hpp" #include "User.hpp" #include "Group.hpp" -#include "Credentials.hpp" +#include "Study.hpp" #include @@ -29,6 +30,8 @@ namespace gde { std::string getServiceURI(const std::string& serviceName) const; + /* UserService */ + const User createUser(const std::string& name, const std::string& password); bool deleteUser(const User&); const User findUser(const std::string& name); @@ -40,6 +43,14 @@ namespace gde { bool addToGroup(const Group&, const User&); bool removeFromGroup(const Group&, const User&); + /* StudyService */ + + const Study createStudy(const std::string& name); + bool deleteStudy(const Study&); + + bool setStudyState(const Study&, int); + const Study readStudy(int); + private: std::string _serverAddress; Credentials _credentials; diff --git a/projects/GDE_API_CPP/api/src/JsonFormatter.cpp b/projects/GDE_API_CPP/api/src/JsonFormatter.cpp index 33d6451..8e3dfd0 100644 --- a/projects/GDE_API_CPP/api/src/JsonFormatter.cpp +++ b/projects/GDE_API_CPP/api/src/JsonFormatter.cpp @@ -1,6 +1,8 @@ #include "JsonFormatter.hpp" #include +#include +#include #include @@ -27,6 +29,23 @@ gde::JsonFormatter::extract(Poco::JSON::Object::Ptr object, const std::string& v } } +Poco::Timestamp +gde::JsonFormatter::extract(Poco::JSON::Object::Ptr object, const std::string& varName) +{ + try { + Poco::Dynamic::Var v = object->get(varName); + std::string formattedDate = v.convert(); + + Poco::DateTime dt; + int tzd; + Poco::DateTimeParser::parse(formattedDate, dt, tzd); + return dt.timestamp(); + } catch (Poco::InvalidAccessException& e) { + // This exception is raised if Var v is empty + return Poco::Timestamp(); // Return current time; not so good, should return nil object + } +} + std::string gde::JsonFormatter::stringify(const Poco::JSON::Object& object) { std::ostringstream jsonStream; @@ -35,5 +54,6 @@ gde::JsonFormatter::stringify(const Poco::JSON::Object& object) { } // Declare methods for the supported template type parameters -template int gde::JsonFormatter::extract(Poco::JSON::Object::Ptr object, const std::string& varName); -template std::string gde::JsonFormatter::extract(Poco::JSON::Object::Ptr object, const std::string& varName); +template int gde::JsonFormatter::extract(Poco::JSON::Object::Ptr, const std::string&); +template bool gde::JsonFormatter::extract(Poco::JSON::Object::Ptr, const std::string&); +template std::string gde::JsonFormatter::extract(Poco::JSON::Object::Ptr, const std::string&); diff --git a/projects/GDE_API_CPP/api/src/JsonFormatter.hpp b/projects/GDE_API_CPP/api/src/JsonFormatter.hpp index 395bbcb..96856cf 100644 --- a/projects/GDE_API_CPP/api/src/JsonFormatter.hpp +++ b/projects/GDE_API_CPP/api/src/JsonFormatter.hpp @@ -1,9 +1,10 @@ -#ifndef JSON_FORMATTER_HPP -#define JSON_FORMATTER_HPP +#ifndef GDE_JSON_FORMATTER_HPP +#define GDE_JSON_FORMATTER_HPP #include #include +#include namespace gde { @@ -11,7 +12,9 @@ namespace gde { public: static Poco::JSON::Object::Ptr parse(const std::string&); + template static T extract(Poco::JSON::Object::Ptr, const std::string&); + static Poco::Timestamp extract(Poco::JSON::Object::Ptr, const std::string&); static std::string stringify(const Poco::JSON::Object&); }; diff --git a/projects/GDE_API_CPP/api/src/Study.hpp b/projects/GDE_API_CPP/api/src/Study.hpp new file mode 100644 index 0000000..1085191 --- /dev/null +++ b/projects/GDE_API_CPP/api/src/Study.hpp @@ -0,0 +1,84 @@ +#ifndef GDE_STUDY_HPP +#define GDE_STUDY_HPP + +#include + +#include + +namespace gde { + + class Study { + friend class StudyService; + friend class GDESession; + + public: + ~Study() {} + + inline int getId() const { return _id; } + inline void setId(int id) { this->_id = id; } + + inline std::string getName() const { return _name; } + inline void setName(const std::string& name) { this->_name = name; } + + inline Poco::Timestamp getCreationDate() const { return _creationDate; } + inline void setCreationDate(const Poco::Timestamp& creationDate) { this->_creationDate = creationDate; } + + inline Poco::Timestamp getUpdateDate() const { return _updateDate; } + inline void setUpdateDate(const Poco::Timestamp& updateDate) { this->_updateDate = updateDate; } + + inline bool getValid() const { return _valid; } + inline void setValid(bool valid) { this->_valid = valid; } + + inline bool getDeleted() const { return _deleted; } + inline void setDeleted(bool deleted) { this->_deleted = deleted; } + + inline Poco::Timestamp getDeletionDate() const { return _deletionDate; } + inline void setDeletionDate(const Poco::Timestamp& deletionDate) { this->_deletionDate = deletionDate; } + + inline int getAttributeGroupId() const { return _attributeGroupId; } + inline void setAttributeGroupId(int attributeGroupId) { this->_attributeGroupId = attributeGroupId; } + + inline int getProfileId() const { return _profileId; } + inline void setProfileId(int profileId) { this->_profileId = profileId; } + + inline bool getLocked() const { return _locked; } + inline void setLocked(bool locked) { this->_locked = locked; } + + inline int getLockOwner() const { return _lockOwner; } + inline void setLockOwner(int lockOwner) { this->_lockOwner = lockOwner; } + + private: + Study(int id=-1, + const std::string& name="", + const Poco::Timestamp& creationDate=Poco::Timestamp(), + const Poco::Timestamp& updateDate=Poco::Timestamp(), + bool valid = false, + bool deleted = false, + const Poco::Timestamp& deletionDate=Poco::Timestamp(), + int attributeGroupId=-1, + int profileId=-1, + bool locked = false, + int lockOwner=-1 + ) + : _id(id), _name(name), _creationDate(creationDate), _updateDate(updateDate), _valid(valid), _deleted(deleted), _deletionDate(deletionDate), _attributeGroupId(attributeGroupId), _profileId(profileId), _locked(locked), _lockOwner(lockOwner) + {} + Study(const Study&); // non copyable + Study& operator=(const Study&); // non copyable + + private: + int _id; + std::string _name; + Poco::Timestamp _creationDate; + Poco::Timestamp _updateDate; + bool _valid; + bool _deleted; + Poco::Timestamp _deletionDate; + int _attributeGroupId; + int _profileId; + bool _locked; + int _lockOwner; + }; + +}; + +#endif diff --git a/projects/GDE_API_CPP/api/src/StudyService.cpp b/projects/GDE_API_CPP/api/src/StudyService.cpp new file mode 100644 index 0000000..7be2f74 --- /dev/null +++ b/projects/GDE_API_CPP/api/src/StudyService.cpp @@ -0,0 +1,110 @@ +#include "StudyService.hpp" +#include "HttpConnection.hpp" +#include "CommandTO.hpp" +#include "CommandResultTO.hpp" +#include "JsonFormatter.hpp" +#include "Credentials.hpp" + +#include +#include +#include + +/* + * WARNING: keep enum values synchronized to WEB API (StudyService) + */ +enum { + CREATE_STUDY = 1, + SET_STUDY_SATE, + READ_STUDY, + DELETE_STUDY +}; + +std::string gde::StudyService::_servletName = "StudyService"; + +const gde::Study +gde::StudyService::createStudy(const std::string& name) +{ + // build JSON string for CommandTO data + Poco::JSON::Object obj; + obj.set("name", Poco::Dynamic::Var(name)); + std::string creationDate = Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::SORTABLE_FORMAT); + obj.set("creationDate", Poco::Dynamic::Var(creationDate)); + std::string data = JsonFormatter::stringify(obj); + + CommandTO cto(CREATE_STUDY, data); + const Credentials& credentials = _session.getCredentials(); + std::string uri = _session.getServiceURI(_servletName); + CommandResultTO crto = gde::HttpConnection(Poco::URI(uri), credentials).doPost(cto); + + // build Study object from CommandResultTO data (json format) + { + Poco::JSON::Object::Ptr object = JsonFormatter::parse(crto.getData()); + int id = JsonFormatter::extract(object, "id"); + std::string name = JsonFormatter::extract(object, "name"); + Poco::Timestamp creationDate = JsonFormatter::extract(object, "creationDate"); + Poco::Timestamp updateDate = JsonFormatter::extract(object, "updateDate"); + bool valid = JsonFormatter::extract(object, "valid"); + bool deleted = JsonFormatter::extract(object, "deleted"); + Poco::Timestamp deletionDate = JsonFormatter::extract(object, "deletionDate"); + int attributeGroupId = JsonFormatter::extract(object, "attributeGroupId"); + int profileId = JsonFormatter::extract(object, "profileId"); + bool locked = JsonFormatter::extract(object, "locked"); + int lockOwner = JsonFormatter::extract(object, "lockOwner"); + + return gde::Study(id, name, creationDate, updateDate, valid, deleted, deletionDate, attributeGroupId, profileId, locked, lockOwner); + } +} + +bool +gde::StudyService::deleteStudy(const Study& study) +{ + CommandTO cto(DELETE_STUDY); + cto.setParameter("studyId", study.getId()); + + const Credentials& credentials = _session.getCredentials(); + std::string uri = _session.getServiceURI(_servletName); + CommandResultTO crto = gde::HttpConnection(Poco::URI(uri), credentials).doPost(cto); + return crto.getCode() == CommandResultTO::OK; +} + +bool +gde::StudyService::setStudyState(const Study& study, int lock) +{ + CommandTO cto(SET_STUDY_SATE); + cto.setParameter("studyId", study.getId()); + cto.setParameter("lock", lock); + + const Credentials& credentials = _session.getCredentials(); + std::string uri = _session.getServiceURI(_servletName); + CommandResultTO crto = gde::HttpConnection(Poco::URI(uri), credentials).doPost(cto); + return crto.getCode() == CommandResultTO::OK; +} + +const gde::Study +gde::StudyService::readStudy(int studyId) +{ + CommandTO cto(READ_STUDY); + cto.setParameter("studyId", studyId); + + const Credentials& credentials = _session.getCredentials(); + std::string uri = _session.getServiceURI(_servletName); + CommandResultTO crto = gde::HttpConnection(Poco::URI(uri), credentials).doPost(cto); + + // build Study object from CommandResultTO data (json format) + { + Poco::JSON::Object::Ptr object = JsonFormatter::parse(crto.getData()); + int id = JsonFormatter::extract(object, "id"); + std::string name = JsonFormatter::extract(object, "name"); + Poco::Timestamp creationDate = JsonFormatter::extract(object, "creationDate"); + Poco::Timestamp updateDate = JsonFormatter::extract(object, "updateDate"); + bool valid = JsonFormatter::extract(object, "valid"); + bool deleted = JsonFormatter::extract(object, "deleted"); + Poco::Timestamp deletionDate = JsonFormatter::extract(object, "deletionDate"); + int attributeGroupId = JsonFormatter::extract(object, "attributeGroupId"); + int profileId = JsonFormatter::extract(object, "profileId"); + bool locked = JsonFormatter::extract(object, "locked"); + int lockOwner = JsonFormatter::extract(object, "lockOwner"); + + return gde::Study(id, name, creationDate, updateDate, valid, deleted, deletionDate, attributeGroupId, profileId, locked, lockOwner); + } +} diff --git a/projects/GDE_API_CPP/api/src/StudyService.hpp b/projects/GDE_API_CPP/api/src/StudyService.hpp new file mode 100644 index 0000000..bfaffab --- /dev/null +++ b/projects/GDE_API_CPP/api/src/StudyService.hpp @@ -0,0 +1,35 @@ +#ifndef GDE_STUDY_SERVICE_HPP +#define GDE_STUDY_SERVICE_HPP + +#include "Study.hpp" +#include "GDESession.hpp" + +#include + +namespace gde { + + class StudyService { + friend class GDESession; + + public: + const Study createStudy(const std::string& name); + bool deleteStudy(const Study&); + + bool setStudyState(const Study&, int lock); + const Study readStudy(int studyId); + + private: + StudyService(const GDESession& session) : _session(session) {} + ~StudyService() {} + StudyService(const StudyService&); // non copyable + StudyService& operator=(const StudyService&); // non copyable + + private: + static std::string _servletName; + const GDESession& _session; + + }; + +}; + +#endif diff --git a/projects/GDE_API_CPP/api/tests/StudyTest.cpp b/projects/GDE_API_CPP/api/tests/StudyTest.cpp new file mode 100644 index 0000000..1cfff75 --- /dev/null +++ b/projects/GDE_API_CPP/api/tests/StudyTest.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include + +void +StudyTest::testCreateDeleteStudy() +{ + gde::GDESession session(serverAddress, credentials); + + const gde::Study& study = session.createStudy("myStudy"); + CPPUNIT_ASSERT(study.getName() == "myStudy"); + CPPUNIT_ASSERT(study.getId() > 0); + + CPPUNIT_ASSERT(session.deleteStudy(study)); +} + +void +StudyTest::testReadStudy() +{ + gde::GDESession session(serverAddress, credentials); + + const gde::Study& study = session.createStudy("myStudy"); + CPPUNIT_ASSERT(study.getId() > 0); + + { + const gde::Study& myStudy = session.readStudy(study.getId()); + CPPUNIT_ASSERT(myStudy.getName() == "myStudy"); + CPPUNIT_ASSERT(myStudy.getId() > 0); + } + + CPPUNIT_ASSERT(session.deleteStudy(study)); + /* + // Will raise an exception: study does not exist anymore + { + const gde::Study& myStudy = session.readStudy(study.getId()); + } + */ +} + +void +StudyTest::testSetStudySate() +{ + gde::GDESession session(serverAddress, credentials); + + const gde::Study& study = session.createStudy("myStudy"); + CPPUNIT_ASSERT(study.getId() > 0); + + CPPUNIT_ASSERT(session.setStudyState(study, 1)); + CPPUNIT_ASSERT(session.setStudyState(study, 0)); + + CPPUNIT_ASSERT(session.deleteStudy(study)); +} diff --git a/projects/GDE_API_CPP/api/tests/StudyTest.hpp b/projects/GDE_API_CPP/api/tests/StudyTest.hpp new file mode 100644 index 0000000..f222950 --- /dev/null +++ b/projects/GDE_API_CPP/api/tests/StudyTest.hpp @@ -0,0 +1,22 @@ +#ifndef GDE_STUDY_TEST_HPP +#define GDE_STUDY_TEST_HPP + +#include + +class StudyTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(StudyTest); + CPPUNIT_TEST(testCreateDeleteStudy); + CPPUNIT_TEST(testReadStudy); + CPPUNIT_TEST(testSetStudySate); + CPPUNIT_TEST_SUITE_END(); + +public: + void testCreateDeleteStudy(); + void testReadStudy(); + void testSetStudySate(); +}; + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(StudyTest, "StudyTest"); + +#endif diff --git a/projects/GDE_API_CPP/api/tests/TestUtilities.hpp b/projects/GDE_API_CPP/api/tests/TestUtilities.hpp index fa5b2be..d210776 100644 --- a/projects/GDE_API_CPP/api/tests/TestUtilities.hpp +++ b/projects/GDE_API_CPP/api/tests/TestUtilities.hpp @@ -5,7 +5,7 @@ #include -std::string serverAddress = "http://localhost:8080/GDE-war"; -gde::Credentials credentials("admin", "edf123"); +static std::string serverAddress = "http://localhost:8080/GDE-war"; +static gde::Credentials credentials("admin", "edf123"); #endif diff --git a/projects/GDE_App/GDE-war/src/java/com/edf/gde/services/BaseService.java b/projects/GDE_App/GDE-war/src/java/com/edf/gde/services/BaseService.java index af303b5..a5a50d9 100644 --- a/projects/GDE_App/GDE-war/src/java/com/edf/gde/services/BaseService.java +++ b/projects/GDE_App/GDE-war/src/java/com/edf/gde/services/BaseService.java @@ -5,13 +5,13 @@ import com.edf.gde.tools.Base64; import com.edf.gde.transferables.CommandTO; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -24,9 +24,11 @@ public abstract class BaseService extends HttpServlet { public abstract void processRequest(HttpServletRequest request, HttpServletResponse response); + private String dateFormat = "yyyy-MM-dd HH:mm:ss"; + protected T read(HttpServletRequest request, Class classOf) { try { - Gson gson = new Gson(); + Gson gson = new GsonBuilder().setDateFormat(dateFormat).create(); return gson.fromJson(request.getReader(), classOf); } catch (IOException ex) { Logger.getLogger(BaseService.class.getName()).log(Level.SEVERE, null, ex); @@ -35,18 +37,18 @@ public abstract class BaseService extends HttpServlet { } protected T fromJson(String json, Class classOf) { - Gson gson = new Gson(); + Gson gson = new GsonBuilder().setDateFormat(dateFormat).create(); return gson.fromJson(json, classOf); } protected String toJson(Object object) { - Gson gson = new Gson(); + Gson gson = new GsonBuilder().setDateFormat(dateFormat).create(); return gson.toJson(object); } protected void send(Object object, HttpServletResponse out) { try { - Gson gson = new Gson(); + Gson gson = new GsonBuilder().setDateFormat(dateFormat).create(); gson.toJson(object, out.getWriter()); } catch (IOException ex) { throw new RuntimeException(ex); -- 2.39.2