From 4841039d4aa78ab4f6ef9484146c49ebab62abdc Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 14 Oct 2014 15:24:24 +0200 Subject: [PATCH 1/4] Renamed void-safe ecf using -safe.ecf convention. Added a cms_demo_module, for now pretty empty, but it will be used to develop/test the cms core. --- cms/{cms.ecf => cms-safe.ecf} | 4 +- examples/api/roc_api.ecf | 4 +- .../modules/demo/cms_demo_module-safe.ecf | 23 +++++ .../roc_api/modules/demo/cms_demo_module.e | 93 +++++++++++++++++++ .../roc_api/{roc_api.ecf => roc_api-safe.ecf} | 5 +- examples/roc_api/src/ewf_roc_server.e | 5 + layout/{layout.ecf => layout-safe.ecf} | 0 ...e_mysql.ecf => persistence_mysql-safe.ecf} | 2 +- .../implementation/mysql/tests/tests.ecf | 2 +- ...sqlite.ecf => persistence_sqlite-safe.ecf} | 2 +- .../tests/{tests.ecf => tests-safe.ecf} | 2 +- 11 files changed, 132 insertions(+), 10 deletions(-) rename cms/{cms.ecf => cms-safe.ecf} (93%) create mode 100644 examples/roc_api/modules/demo/cms_demo_module-safe.ecf create mode 100644 examples/roc_api/modules/demo/cms_demo_module.e rename examples/roc_api/{roc_api.ecf => roc_api-safe.ecf} (91%) rename layout/{layout.ecf => layout-safe.ecf} (100%) rename persistence/implementation/mysql/{persistence_mysql.ecf => persistence_mysql-safe.ecf} (96%) rename persistence/implementation/sqlite/{persistence_sqlite.ecf => persistence_sqlite-safe.ecf} (96%) rename persistence/implementation/sqlite/tests/{tests.ecf => tests-safe.ecf} (97%) diff --git a/cms/cms.ecf b/cms/cms-safe.ecf similarity index 93% rename from cms/cms.ecf rename to cms/cms-safe.ecf index dc20d24..235bd74 100644 --- a/cms/cms.ecf +++ b/cms/cms-safe.ecf @@ -9,9 +9,9 @@ - + - + diff --git a/examples/api/roc_api.ecf b/examples/api/roc_api.ecf index adf512d..a68d4e1 100644 --- a/examples/api/roc_api.ecf +++ b/examples/api/roc_api.ecf @@ -16,12 +16,12 @@ - + - + diff --git a/examples/roc_api/modules/demo/cms_demo_module-safe.ecf b/examples/roc_api/modules/demo/cms_demo_module-safe.ecf new file mode 100644 index 0000000..dd678f2 --- /dev/null +++ b/examples/roc_api/modules/demo/cms_demo_module-safe.ecf @@ -0,0 +1,23 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + diff --git a/examples/roc_api/modules/demo/cms_demo_module.e b/examples/roc_api/modules/demo/cms_demo_module.e new file mode 100644 index 0000000..aeabe7b --- /dev/null +++ b/examples/roc_api/modules/demo/cms_demo_module.e @@ -0,0 +1,93 @@ +note + description: "Summary description for {CMS_DEMO_MODULE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_DEMO_MODULE + +inherit + CMS_MODULE + +create + make + +feature {NONE} -- Initialization + + make (a_config: CMS_SETUP) + do + name := "Demo module" + version := "1.0" + description := "Service to demonstrate and test cms system" + package := "demo" + config := a_config + end + + config: CMS_SETUP + -- Node configuration. + +feature -- Access: router + + router: WSF_ROUTER + -- Node router. + do + create Result.make (2) + + map_uri_template_agent (Result, "/demo/", agent handle_demo) + map_uri_template_agent (Result, "/book/{id}", agent handle_demo_entry) + end + +feature -- Handler + + handle_demo, + handle_demo_entry (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_NOT_FOUND_RESPONSE + do + create m.make (req) + m.set_body ("Not yet implemented!") + res.send (m) + end + +feature -- Mapping helper: uri template + + map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER) + -- Map `h' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + map_uri_template_with_request_methods (a_router, a_tpl, h, Void) + end + + map_uri_template_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `h' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + a_router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods) + end + +feature -- Mapping helper: uri template agent + + map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + -- Map `proc' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_agent_with_request_methods (a_router, a_tpl, proc, Void) + end + + map_uri_template_agent_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_with_request_methods (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods) + end + +end diff --git a/examples/roc_api/roc_api.ecf b/examples/roc_api/roc_api-safe.ecf similarity index 91% rename from examples/roc_api/roc_api.ecf rename to examples/roc_api/roc_api-safe.ecf index fb9f85c..c8576e2 100644 --- a/examples/roc_api/roc_api.ecf +++ b/examples/roc_api/roc_api-safe.ecf @@ -10,8 +10,9 @@ - - + + + diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 1e02a20..4919a54 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -137,6 +137,11 @@ feature -- CMS setup create {NODE_MODULE} m.make (a_setup) m.enable a_setup.modules.extend (m) + + create {CMS_DEMO_MODULE} m.make (a_setup) + m.enable + a_setup.modules.extend (m) + end setup_storage (a_setup: CMS_SETUP) diff --git a/layout/layout.ecf b/layout/layout-safe.ecf similarity index 100% rename from layout/layout.ecf rename to layout/layout-safe.ecf diff --git a/persistence/implementation/mysql/persistence_mysql.ecf b/persistence/implementation/mysql/persistence_mysql-safe.ecf similarity index 96% rename from persistence/implementation/mysql/persistence_mysql.ecf rename to persistence/implementation/mysql/persistence_mysql-safe.ecf index d66d48e..6811629 100644 --- a/persistence/implementation/mysql/persistence_mysql.ecf +++ b/persistence/implementation/mysql/persistence_mysql-safe.ecf @@ -12,7 +12,7 @@ - + diff --git a/persistence/implementation/mysql/tests/tests.ecf b/persistence/implementation/mysql/tests/tests.ecf index cf85bf2..363c33c 100644 --- a/persistence/implementation/mysql/tests/tests.ecf +++ b/persistence/implementation/mysql/tests/tests.ecf @@ -8,7 +8,7 @@ - + diff --git a/persistence/implementation/sqlite/persistence_sqlite.ecf b/persistence/implementation/sqlite/persistence_sqlite-safe.ecf similarity index 96% rename from persistence/implementation/sqlite/persistence_sqlite.ecf rename to persistence/implementation/sqlite/persistence_sqlite-safe.ecf index c4c203c..90af783 100644 --- a/persistence/implementation/sqlite/persistence_sqlite.ecf +++ b/persistence/implementation/sqlite/persistence_sqlite-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/persistence/implementation/sqlite/tests/tests.ecf b/persistence/implementation/sqlite/tests/tests-safe.ecf similarity index 97% rename from persistence/implementation/sqlite/tests/tests.ecf rename to persistence/implementation/sqlite/tests/tests-safe.ecf index d401b33..cdeb22e 100644 --- a/persistence/implementation/sqlite/tests/tests.ecf +++ b/persistence/implementation/sqlite/tests/tests-safe.ecf @@ -8,7 +8,7 @@ - + From b8257e7bf07bc69be223cffa87256208e4be76ae Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 8 Oct 2014 16:33:41 -0300 Subject: [PATCH 2/4] Clean code: removed unused variables. Move BASIC_AUTH module to ROC_SERVER setup, as an example of module extension --- cms/src/configuration/cms_default_setup.e | 4 ---- cms/src/modules/basic_auth/handler/basic_auth_login_handler.e | 2 -- cms/src/modules/node/handler/node_content_handler.e | 3 --- cms/src/modules/node/handler/node_handler.e | 1 - cms/src/modules/node/handler/node_summary_handler.e | 2 -- cms/src/modules/node/handler/node_title_handler.e | 2 -- cms/src/service/cms_api_service.e | 2 -- cms/src/service/response/cms_generic_response.e | 1 - examples/roc_api/src/ewf_roc_server.e | 3 +-- 9 files changed, 1 insertion(+), 19 deletions(-) diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index e1bf624..8918a36 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -62,10 +62,6 @@ feature {NONE} -- Initialization -- modules.extend (m) - create {BASIC_AUTH_MODULE} m.make (Current) - m.enable - modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) diff --git a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e index a0e6df8..4035e0f 100644 --- a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e +++ b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e @@ -47,8 +47,6 @@ feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do log.write_information(generator + ".do_get Processing basic auth login") if attached {STRING_32} current_user_name (req) as l_user then diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index a7113b7..31b911a 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -88,8 +88,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then @@ -116,7 +114,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check if user has permissions") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e index e9d789f..e4257b6 100644 --- a/cms/src/modules/node/handler/node_handler.e +++ b/cms/src/modules/node/handler/node_handler.e @@ -85,7 +85,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check user permissions!!!") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index d3ec258..96f3c36 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -87,8 +87,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index 570b155..a5dfe9d 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -86,8 +86,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/service/cms_api_service.e b/cms/src/service/cms_api_service.e index c7b69ff..a7d4c8f 100644 --- a/cms/src/service/cms_api_service.e +++ b/cms/src/service/cms_api_service.e @@ -29,8 +29,6 @@ feature -- Initialize feature -- Access is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN - local - l_security: SECURITY_PROVIDER do Result := storage.is_valid_credential (l_auth_login, l_auth_password) end diff --git a/cms/src/service/response/cms_generic_response.e b/cms/src/service/response/cms_generic_response.e index 380a391..371bc05 100644 --- a/cms/src/service/response/cms_generic_response.e +++ b/cms/src/service/response/cms_generic_response.e @@ -50,7 +50,6 @@ feature -- Responses -- Handle not authorized. local h: HTTP_HEADER - output: STRING do create h.make h.put_content_type_text_html diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 4919a54..5fd497b 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -117,7 +117,6 @@ feature -- CMS Initialization initialize_cms (a_setup: CMS_SETUP) local cms: CMS_SERVICE - l_modules: CMS_MODULE_COLLECTION do log.write_debug (generator + ".initialize_cms") @@ -134,7 +133,7 @@ feature -- CMS setup local m: CMS_MODULE do - create {NODE_MODULE} m.make (a_setup) + create {BASIC_AUTH_MODULE} m.make (a_setup) m.enable a_setup.modules.extend (m) From 4b9a692c2cb8c5f197d4d1c18ff4acbd7072b116 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 9 Oct 2014 10:01:49 -0300 Subject: [PATCH 3/4] Initial Import: Database transaction handling. Initial Import: Error handling using ewf error library. Added a CMS_ERROR_FILTER as part of registered filters of CMS_SERVICE --- cms/cms-safe.ecf | 1 + cms/src/configuration/cms_default_setup.e | 40 ++++++- cms/src/configuration/cms_setup.e | 6 + cms/src/service/cms_service.e | 6 + cms/src/service/filter/cms_error_filter.e | 33 ++++++ .../common/database/database_connection.e | 61 +++++++++- .../common/database/database_handler.e | 55 +++++++-- .../common/database/database_handler_impl.e | 107 +++++------------- .../database/database_iteration_cursor.e | 2 +- .../database/database_no_change_error.e | 27 +++++ .../common/database/database_query.e | 27 +---- .../common/database/error/database_error.e | 23 ++++ .../{ => error}/database_error_handler.e | 16 +-- .../{ => error}/shared_error_handler.e | 14 ++- .../mysql/src/cms_storage_mysql.e | 6 + .../src/database/database_connection_mysql.e | 16 +-- .../mysql/src/provider/node_data_provider.e | 8 -- .../mysql/src/provider/role_data_provider.e | 10 +- .../mysql/src/provider/user_data_provider.e | 8 -- .../mysql/tests/util/clean_db.e | 2 + 20 files changed, 301 insertions(+), 167 deletions(-) create mode 100644 cms/src/service/filter/cms_error_filter.e create mode 100644 persistence/implementation/common/database/database_no_change_error.e create mode 100644 persistence/implementation/common/database/error/database_error.e rename persistence/implementation/common/database/{ => error}/database_error_handler.e (60%) rename persistence/implementation/common/database/{ => error}/shared_error_handler.e (78%) diff --git a/cms/cms-safe.ecf b/cms/cms-safe.ecf index 235bd74..5da317c 100644 --- a/cms/cms-safe.ecf +++ b/cms/cms-safe.ecf @@ -10,6 +10,7 @@ + diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 8918a36..eabc20c 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -17,6 +17,7 @@ feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) do + create error_handler.make layout := a_layout create configuration.make (layout) initialize @@ -28,7 +29,6 @@ feature {NONE} -- Initialization create modules.make (3) build_api_service build_mailer - initialize_modules end @@ -61,7 +61,6 @@ feature {NONE} -- Initialization -- m.enable -- modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) @@ -89,15 +88,44 @@ feature -- Access build_api_service local l_database: DATABASE_CONNECTION + l_retry: BOOLEAN + l_message: STRING do - to_implement ("Refactor database setup") - if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then - create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) - create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + if not l_retry then + to_implement ("Refactor database setup") + if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) + create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + else + create {DATABASE_CONNECTION_NULL} l_database.make_common + create api_service.make (create {CMS_STORAGE_NULL}) + end else + to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.") + -- error hanling. create {DATABASE_CONNECTION_NULL} l_database.make_common create api_service.make (create {CMS_STORAGE_NULL}) + create l_message.make (1024) + if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then + if attached l_exception.description as l_description then + l_message.append (l_description.as_string_32) + l_message.append ("%N%N") + elseif attached l_exception.trace as l_trace then + l_message.append (l_trace) + l_message.append ("%N%N") + else + l_message.append (l_exception.out) + l_message.append ("%N%N") + end + else + l_message.append ("The application crash without available information") + l_message.append ("%N%N") + end + error_handler.add_custom_error (0, " Database Connection ", l_message) end + rescue + l_retry := True + retry end build_auth_engine diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index f0af327..0e6189d 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -32,6 +32,12 @@ feature -- Access deferred end + +feature -- Status Report + + error_handler: ERROR_HANDLER + -- Error handler. + feature -- Access: Site site_id: READABLE_STRING_8 diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 37a4759..84b67e6 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -165,9 +165,15 @@ feature -- Filters do log.write_debug (generator + ".create_filter") l_filter := Void + -- Maintenance create {WSF_MAINTENANCE_FILTER} f f.set_next (l_filter) + l_filter := f + + -- Error Filter + create {CMS_ERROR_FILTER} f.make (setup) + f.set_next (l_filter) l_filter := f -- Include filters from modules diff --git a/cms/src/service/filter/cms_error_filter.e b/cms/src/service/filter/cms_error_filter.e new file mode 100644 index 0000000..8a2f154 --- /dev/null +++ b/cms/src/service/filter/cms_error_filter.e @@ -0,0 +1,33 @@ +note + description: "Summary description for {CMS_ERROR_FILTER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_ERROR_FILTER + +inherit + + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + do + if not setup.error_handler.has_error then + log.write_information (generator + ".execute") + execute_next (req, res) + else + log.write_critical (generator + ".execute" + setup.error_handler.as_string_representation ) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + setup.error_handler.reset + end + end + +end diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index 7591273..cfb9fd9 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -10,7 +10,7 @@ inherit DATABASE_CONFIG - SHARED_ERROR + SHARED_ERROR_HANDLER feature {NONE} -- Initialization @@ -72,6 +72,65 @@ feature -- Database Setup keep_connection: BOOLEAN -- Keep connection alive? +feature -- Transactions + + begin_transaction + -- Start a transaction which will be terminated by a call to `rollback' or `commit'. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.begin + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + commit + -- Commit updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.commit + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + rollback + -- Rollback updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.rollback + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + feature -- Change Element not_keep_connection diff --git a/persistence/implementation/common/database/database_handler.e b/persistence/implementation/common/database/database_handler.e index 3551e4a..875b7a7 100644 --- a/persistence/implementation/common/database/database_handler.e +++ b/persistence/implementation/common/database/database_handler.e @@ -8,7 +8,7 @@ deferred class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER feature -- Access @@ -42,15 +42,15 @@ feature -- Modifiers feature -- Functionality Store Procedures - execute_reader - -- Execute store. + execute_store_reader + -- Execute a `store' to read data. require store_not_void: store /= void deferred end - execute_writer - -- Execute store. + execute_store_writer + -- Execute a `store' to write data. require store_not_void: store /= void deferred @@ -59,14 +59,14 @@ feature -- Functionality Store Procedures feature -- SQL Queries execute_query - -- Execute query. + -- Execute sql query, the read data from the database. require query_not_void: query /= void deferred end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql query that update/add data. require query_not_void: query /= void deferred @@ -147,14 +147,49 @@ feature -- Access feature -- Status Report - has_error: BOOLEAN - -- Is there an error? - count: INTEGER -- Number of rows, last execution. deferred end + connection: DATABASE_CONNECTION + -- Database connection. + + db_control: DB_CONTROL + -- Database control. + do + Result := connection.db_control + end + + db_result: detachable DB_RESULT + -- Database query result. + + db_selection: detachable DB_SELECTION + -- Database selection. + + db_change: detachable DB_CHANGE + -- Database modification. + +feature -- Error handling + + check_database_change_error + -- Check database error from `db_change'. + do + if attached db_change as l_change and then not l_change.is_ok then + database_error_handler.add_database_error (l_change.error_message_32, l_change.error_code) + log.write_error (generator + ".check_database_change_error: " + l_change.error_message_32) + end + end + + check_database_selection_error + -- Check database error from `db_selection'. + do + if attached db_selection as l_selection and then not l_selection.is_ok then + database_error_handler.add_database_error (l_selection.error_message_32, l_selection.error_code) + log.write_error (generator + ".check_database_selection_error: " + l_selection.error_message_32) + end + end + feature {NODE_DATA_PROVIDER}-- Implementation connect diff --git a/persistence/implementation/common/database/database_handler_impl.e b/persistence/implementation/common/database/database_handler_impl.e index 96078ee..55bde06 100644 --- a/persistence/implementation/common/database/database_handler_impl.e +++ b/persistence/implementation/common/database/database_handler_impl.e @@ -21,7 +21,6 @@ feature {NONE} -- Initialization do connection := a_connection create last_query.make_now - set_successful ensure connection_not_void: connection /= Void last_query_not_void: last_query /= Void @@ -29,71 +28,52 @@ feature {NONE} -- Initialization feature -- Functionality - execute_reader + execute_store_reader -- Execute stored procedure that returns data. local l_db_selection: DB_SELECTION l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached store as l_store then create l_db_selection.make db_selection := l_db_selection items := l_store.execute_reader (l_db_selection) + check_database_selection_error end - - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_reader Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_reader " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end - execute_writer + execute_store_writer -- Execute stored procedure that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end if attached store as l_store then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_store.execute_writer (l_db_change) - if not l_store.has_error then - db_control.commit - end + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_writer Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -106,60 +86,43 @@ feature -- SQL Queries l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached query as l_query then create l_db_selection.make db_selection := l_db_selection items := l_query.execute_reader (l_db_selection) + check_database_selection_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("execute_query") - log.write_critical (generator+ ".execute_query " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql_query that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end - if attached query as l_query then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_query.execute_change (l_db_change) - db_control.commit + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -207,24 +170,6 @@ feature -- Iteration feature {NONE} -- Implementation - connection: DATABASE_CONNECTION - -- Database connection. - - db_control: DB_CONTROL - -- Database control. - do - Result := connection.db_control - end - - db_result: detachable DB_RESULT - -- Database query result. - - db_selection: detachable DB_SELECTION - -- Database selection. - - db_update: detachable DB_CHANGE - -- Database modification. - last_query: DATE_TIME -- Last time when a query was executed. @@ -263,7 +208,7 @@ feature {NONE} -- Implementation affected_row_count: INTEGER -- The number of rows changed, deleted, or inserted by the last statement. do - if attached db_update as l_update then + if attached db_change as l_update then Result := l_update.affected_row_count end end diff --git a/persistence/implementation/common/database/database_iteration_cursor.e b/persistence/implementation/common/database/database_iteration_cursor.e index f38d844..1688b76 100644 --- a/persistence/implementation/common/database/database_iteration_cursor.e +++ b/persistence/implementation/common/database/database_iteration_cursor.e @@ -1,5 +1,5 @@ note - description: "External iteration cursor for {ESA_DATABASE_HANDLER}" + description: "External iteration cursor for {DATABASE_HANDLER}" date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" revision: "$Revision: 95678 $" diff --git a/persistence/implementation/common/database/database_no_change_error.e b/persistence/implementation/common/database/database_no_change_error.e new file mode 100644 index 0000000..6855279 --- /dev/null +++ b/persistence/implementation/common/database/database_no_change_error.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {DATABASE_NO_CHANGE_ERROR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DATABASE_NO_CHANGE_ERROR + +inherit + DATABASE_ERROR + redefine + make_from_message + end + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database No Change Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_query.e b/persistence/implementation/common/database/database_query.e index 1a5f8cd..4acec0c 100644 --- a/persistence/implementation/common/database/database_query.e +++ b/persistence/implementation/common/database/database_query.e @@ -8,14 +8,14 @@ class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER REFACTORING_HELPER create data_reader -feature -- Intialization +feature {NONE} -- Intialization data_reader (a_query: STRING; a_parameters: STRING_TABLE [detachable ANY]) -- SQL data reader for the query `a_query' with arguments `a_parameters' @@ -29,6 +29,8 @@ feature -- Intialization parameters_set: parameters = a_parameters end +feature -- Execution + execute_reader (a_base_selection: DB_SELECTION): detachable LIST [DB_RESULT] -- Execute the Current sql query. do @@ -42,7 +44,6 @@ feature -- Intialization a_base_selection.load_result Result := a_base_selection.container else - set_last_error (a_base_selection.error_message_32, generator + ".execute_reader" ) log.write_error (generator + "." + a_base_selection.error_message_32) end unset_map_name (a_base_selection) @@ -50,17 +51,12 @@ feature -- Intialization end execute_change (a_base_change: DB_CHANGE) - -- Execute the Current sql query . + -- Execute the Current sql query to change/update data in the database. do to_implement ("Check test dynamic sequel. to redesign.") set_map_name (a_base_change) a_base_change.set_query (query) a_base_change.execute_query - if a_base_change.is_ok then - else - set_last_error (a_base_change.error_message_32, generator + ".execute_reader" ) - log.write_error (generator + "." + a_base_change.error_message_32) - end unset_map_name (a_base_change) end @@ -72,17 +68,6 @@ feature -- Access parameters: STRING_TABLE [detachable ANY] -- query parameters. -feature -- Status Report - - has_error: BOOLEAN - -- is there an error? - - error_message: detachable STRING_32 - -- Error message if any. - - error_code: INTEGER - -- Error code. - feature {NONE} -- Implementation set_map_name (a_base_selection: DB_EXPRESSION) @@ -140,4 +125,4 @@ feature {NONE} -- Implementation end -end -- ESA_DATABASE_QUERY +end -- DATABASE_QUERY diff --git a/persistence/implementation/common/database/error/database_error.e b/persistence/implementation/common/database/error/database_error.e new file mode 100644 index 0000000..2dcd646 --- /dev/null +++ b/persistence/implementation/common/database/error/database_error.e @@ -0,0 +1,23 @@ +note + description: "Error from database" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + DATABASE_ERROR + +inherit + ERROR_CUSTOM + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_error_handler.e b/persistence/implementation/common/database/error/database_error_handler.e similarity index 60% rename from persistence/implementation/common/database/database_error_handler.e rename to persistence/implementation/common/database/error/database_error_handler.e index a2718fe..5af3545 100644 --- a/persistence/implementation/common/database/database_error_handler.e +++ b/persistence/implementation/common/database/error/database_error_handler.e @@ -15,21 +15,21 @@ create feature -- Error operation add_database_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_ERROR + l_error: DATABASE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end add_database_no_change_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_NO_CHANGE_ERROR + l_error: DATABASE_NO_CHANGE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end end diff --git a/persistence/implementation/common/database/shared_error_handler.e b/persistence/implementation/common/database/error/shared_error_handler.e similarity index 78% rename from persistence/implementation/common/database/shared_error_handler.e rename to persistence/implementation/common/database/error/shared_error_handler.e index 9c26bfd..15c06c1 100644 --- a/persistence/implementation/common/database/shared_error_handler.e +++ b/persistence/implementation/common/database/error/shared_error_handler.e @@ -6,14 +6,26 @@ note class SHARED_ERROR_HANDLER +inherit + + SHARED_LOGGER + feature -- Access database_error_handler: DATABASE_ERROR_HANDLER - -- Error handler + -- Error handler. once create Result.make end +feature -- Status Report + + has_error: BOOLEAN + -- Has error? + do + Result := database_error_handler.has_error + end + feature -- Helper exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index 74a72ce..47b74ca 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -21,6 +21,7 @@ feature {NONE} -- Initialization require is_connected: a_connection.is_connected do + connection := a_connection log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) create node_provider.make (a_connection) create user_provider.make (a_connection) @@ -125,11 +126,14 @@ feature -- Change: user save_user (a_user: CMS_USER) -- Add a new user `a_user'. do + if attached a_user.password as l_password and then attached a_user.email as l_email then + connection.begin_transaction user_provider.new_user (a_user.name, l_password, l_email) + connection.commit else set_last_error ("User or Password not attached", generator + ".save_user") end @@ -282,4 +286,6 @@ feature {NONE} -- Post process user_provider: USER_DATA_PROVIDER -- User Data provider. + connection: DATABASE_CONNECTION + -- Current database connection. end diff --git a/persistence/implementation/mysql/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e index bc5f890..b9d173f 100644 --- a/persistence/implementation/mysql/src/database/database_connection_mysql.e +++ b/persistence/implementation/mysql/src/database/database_connection_mysql.e @@ -34,17 +34,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end @@ -66,17 +60,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 3bcfc84..59c9bae 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end feature -- Access @@ -481,13 +480,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/src/provider/role_data_provider.e b/persistence/implementation/mysql/src/provider/role_data_provider.e index 61590a0..8e82a64 100644 --- a/persistence/implementation/mysql/src/provider/role_data_provider.e +++ b/persistence/implementation/mysql/src/provider/role_data_provider.e @@ -35,7 +35,7 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful +-- Result := db_handler.successful end has_roles: BOOLEAN @@ -210,13 +210,7 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end + end end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index a1ac47b..0d9b0bf 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end has_user: BOOLEAN @@ -311,13 +310,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index 965465f..dce7176 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -24,6 +24,7 @@ feature do create l_parameters.make (0) + a_connection.begin_transaction -- Clean Profiles db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_user_profiles, l_parameters)) @@ -72,6 +73,7 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_profiles_autoincrement, l_parameters)) db_handler(a_connection).execute_change + a_connection.commit end From ab76a752d787f1b2f75879251a027a7fbf63f44d Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 13 Oct 2014 18:45:45 -0300 Subject: [PATCH 4/4] Added nested transaction support. Added test cases showing transaction support scenarios. --- .../common/database/database_connection.e | 136 ++++++++++++++ .../tests/transactions/transaction_test_set.e | 168 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 persistence/implementation/mysql/tests/transactions/transaction_test_set.e diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index cfb9fd9..a6c2d2c 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -131,6 +131,142 @@ feature -- Transactions retry end + +feature -- + + is_connected_to_storage: BOOLEAN + -- Is connected to the database + do + Result := db_control.is_connected + end + +feature -- Transaction Status + + in_transaction_session: BOOLEAN + -- Is session started? + + transaction_session_depth: INTEGER + -- Depth in the transaction session + +feature -- Transaction Operation + + begin_transaction2 + -- Start session + -- if already started, increase the `transaction_session_depth' + require + in_transaction_session implies transaction_session_depth > 0 + not in_transaction_session implies transaction_session_depth = 0 + local + l_session_control: like db_control + l_retried: INTEGER + do + if l_retried = 0 then + if not in_transaction_session then + database_error_handler.reset + + check transaction_session_depth = 0 end + + debug ("database_session") + print ("..Start session%N") + end + connect -- connect the DB + if is_connected_to_storage then + in_transaction_session := True + db_control.begin -- start transaction + else + l_session_control := db_control + if not l_session_control.is_ok then + database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) + else + database_error_handler.add_database_error ("Session_not_started_error_message", 0) + end + l_session_control.reset + end + end + transaction_session_depth := transaction_session_depth + 1 + else + if l_retried = 1 then + transaction_session_depth := transaction_session_depth + 1 + if attached (create {EXCEPTION_MANAGER}).last_exception as e then + if attached {ASSERTION_VIOLATION} e then + --| Ignore for now with MYSQL ... + else + exception_as_error (e) + end + end + + in_transaction_session := False + db_control.reset + else + in_transaction_session := False + end + end + ensure + transaction_session_depth = (old transaction_session_depth) + 1 + rescue + l_retried := l_retried + 1 + retry + end + + commit2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + if not database_error_handler.has_error then + db_control.commit -- Commit transaction + else + db_control.rollback -- Rollback transaction + end + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end + + + + rollback2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + db_control.rollback -- Rollback transaction + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end feature -- Change Element not_keep_connection diff --git a/persistence/implementation/mysql/tests/transactions/transaction_test_set.e b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e new file mode 100644 index 0000000..3199f71 --- /dev/null +++ b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e @@ -0,0 +1,168 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TRANSACTION_TEST_SET + +inherit + + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_user_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + end + + test_user_node_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + node_provider.add_author (1, 1) + assert ("Has one node:", node_provider.count = 1) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + assert ("Not has nodes:", node_provider.count = 0) + end + + + + test_user_node_nested_transaction_with_rollback_not_supported + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.rollback2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has nodes:", node_provider.count = 1) + assert ("Not has user: test1", user_provider.user_by_name ("test1") = Void) + end + + + test_user_node_nested_transaction_with_commit + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has user test1:", attached user_provider.user_by_name ("test1")) + assert ("Has nodes:", node_provider.count = 1) + end + + + test_user_node_nested_transaction_with_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.rollback2 + assert ("Not Has user test:", user_provider.user_by_name ("test") = void) + assert ("Not Has user test1:", user_provider.user_by_name ("test1") = void) + assert ("Has 0 nodes:", node_provider.count = 0) + end + + + + + +feature {NONE} -- Implementation + + node_provider: NODE_DATA_PROVIDER + -- node provider. + once + create Result.make (connection) + end + + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end + + +feature {NONE} -- Implementation Fixture Factories + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end +end + +