indexing description: "API interface to issue services." author: "Peter Wyss " date: "$Date: 2007-07-08 11:41:12 +0200 (Sun, 08 Jul 2007) $" revision: "$Revision: 452 $" class ISSUE_SERVICE inherit API_SERVICE redefine make end create make feature {NONE} -- Initialization make (a_node: O_NODE_CLIENT) is -- Creates the service do precursor(a_node) is_internal := false end feature -- Basic operations issue_add (a_session: STRING; a_project: INTEGER_REF; a_title: STRING; a_description: STRING; a_tags: STRING; a_is_private: BOOLEAN_REF): INTEGER_REF is -- Create a new issue and issue workitem. local l_msg: O_ISSUE_ADD_MESSAGE l_issue_add_reply_msg: O_GENERIC_MESSAGE do Result := 0 -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_title = Void or else a_title.is_empty then last_fault := err_invalid_title elseif a_description = Void then last_fault := err_invalid_description elseif not valid_tags (a_tags) then last_fault := err_invalid_tags else -- generate and send message create l_msg.make (a_session, a_project, a_title, a_description, a_tags, a_is_private, is_internal) send_and_wait_for_reply (l_msg) if is_ok then l_issue_add_reply_msg ?= last_reply check valid_reply: l_issue_add_reply_msg /= Void end create Result Result.set_item (l_issue_add_reply_msg.arguments.item ("project_issue_id").to_integer) end end end issue_update (a_session: STRING; a_project: INTEGER_REF; a_issue_id: INTEGER_REF; a_title: STRING; a_description: STRING; a_tags: STRING; a_is_private: BOOLEAN_REF): BOOLEAN_REF is -- update an issue and issue workitem. local l_msg: O_ISSUE_UPDATE_MESSAGE l_issue_update_reply_msg: O_GENERIC_MESSAGE do Result := False -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_issue_id <= 0 then last_fault := err_invalid_issue elseif a_title = Void or else a_title.is_empty then last_fault := err_invalid_title elseif a_description = Void then last_fault := err_invalid_description elseif not valid_tags (a_tags) then last_fault := err_invalid_tags else -- generate and send message create l_msg.make (a_session, a_project, a_issue_id, a_title, a_description, a_tags, a_is_private, is_internal) send_and_wait_for_reply (l_msg) if is_ok then l_issue_update_reply_msg ?= last_reply create Result Result.set_item (True) end end end issue_comment (a_session: STRING; a_project: INTEGER_REF; a_project_issue_id: INTEGER_REF; a_description: STRING): BOOLEAN_REF is -- Add reply to an issue without changing its tags local l_msg: O_ISSUE_COMMENT_MESSAGE do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_project_issue_id <= 0 then last_fault := err_invalid_issue elseif a_description = Void then last_fault := err_invalid_description else -- generate and send message create l_msg.make (a_session, a_project, a_project_issue_id, a_description, is_internal) send_and_wait_for_reply (l_msg) if is_ok then create Result Result.set_item (True) end end end issue_comment_extended (a_session: STRING; a_project: INTEGER_REF; a_project_issue_id: INTEGER_REF; a_description: STRING; a_tags: STRING): BOOLEAN_REF is -- Add reply to an issue which contains changed tags local l_msg: O_ISSUE_COMMENT_MESSAGE do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_project_issue_id <= 0 then last_fault := err_invalid_issue elseif a_description = Void then last_fault := err_invalid_description elseif not valid_tags (a_tags) then last_fault := err_invalid_tags else -- generate and send message create l_msg.make_extended (a_session, a_project, a_project_issue_id, a_description, a_tags, is_internal) send_and_wait_for_reply (l_msg) if is_ok then create Result Result.set_item (True) end end end issue_list (a_session: STRING; a_project: INTEGER_REF): ARRAY [DS_HASH_TABLE [ANY, STRING]] is -- List issues for a_project local l_msg: O_ISSUE_LIST_MESSAGE l_issue_list_reply_msg: O_ISSUE_LIST_REPLY_MESSAGE l_ilist: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING], INTEGER] l_issue_t: TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING] l_issue: DS_HASH_TABLE [ANY, STRING] i: INTEGER l_issue_id_ref: INTEGER_REF l_creation_time_ref: INTEGER_REF l_last_modified_ref: INTEGER_REF do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project else create l_msg.make (a_session, a_project) send_and_wait_for_reply (l_msg) if is_ok then l_issue_list_reply_msg ?= last_reply check issue_list_reply_message: l_issue_list_reply_msg /= Void end -- TODO Use convert_issue_list for conversion l_ilist := l_issue_list_reply_msg.issue_list create Result.make (0, l_ilist.count-1) from i := 0 until i >= l_ilist.count loop l_issue_t := l_ilist.item (i) create l_issue.make (5) create l_issue_id_ref l_issue_id_ref.set_item (l_issue_t.issue_id) l_issue.force (l_issue_id_ref, "issue_id") create l_creation_time_ref l_creation_time_ref.set_item (l_issue_t.creation_time) l_issue.force (l_creation_time_ref, "creation_time") create l_last_modified_ref l_last_modified_ref.set_item (l_issue_t.last_modified) l_issue.force (l_last_modified_ref, "last_modified") l_issue.force (l_issue_t.user, "user") l_issue.force (l_issue_t.title, "title") l_issue.force (l_issue_t.tags, "tags") Result.force (l_issue, i) i := i + 1 end end end end issue_list_changed (a_session: STRING; a_project: INTEGER_REF; a_date: INTEGER_REF): ARRAY [DS_HASH_TABLE [ANY, STRING]] is -- List issues in a_project that have changed since a_date local l_msg: O_ISSUE_LIST_CHANGED_MESSAGE l_issue_list_reply_msg: O_ISSUE_LIST_REPLY_MESSAGE do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_date < 0 then last_fault := err_invalid_date else create l_msg.make (a_session, a_project, a_date) send_and_wait_for_reply (l_msg) if is_ok then l_issue_list_reply_msg ?= last_reply check issue_list_reply_message: l_issue_list_reply_msg /= Void end Result := convert_issue_list (l_issue_list_reply_msg.issue_list) end end end issue_list_tags (a_session: STRING; a_project: INTEGER_REF): ARRAY [STRING] is -- List all tags used in a_project local l_msg: O_ISSUE_LIST_TAGS_MESSAGE l_issue_list_tags_reply_msg: O_ISSUE_LIST_TAGS_REPLY_MESSAGE do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project else create l_msg.make (a_session, a_project) send_and_wait_for_reply (l_msg) if is_ok then l_issue_list_tags_reply_msg ?= last_reply check issue_list_tags_reply_message: l_issue_list_tags_reply_msg /= Void end Result := l_issue_list_tags_reply_msg.tag_list end end end issue_retrieve (a_session: STRING; a_issue_id: INTEGER_REF; a_project: INTEGER_REF): DS_HASH_TABLE [ANY, STRING] is -- Retrieve issue local l_msg: O_ISSUE_RETRIEVE_MESSAGE l_issue_reply_msg: O_ISSUE_RETRIEVE_REPLY_MESSAGE l_issue_revisions: ARRAYED_LIST [TUPLE [creation_time: INTEGER; user: STRING; text: STRING; tags: STRING]] l_issue_revision: TUPLE [creation_time: INTEGER; user: STRING; text: STRING; tags: STRING] i: INTEGER l_issue_id_ref: INTEGER_REF l_creation_time_ref: INTEGER_REF l_last_modified_ref: INTEGER_REF l_is_private_ref: BOOLEAN_REF l_revision_count_ref: INTEGER_REF l_rev_creation_time_ref: INTEGER_REF do -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_issue_id <= 0 then last_fault := err_invalid_issue elseif a_project <= 0 then last_fault := err_invalid_project else create l_msg.make (a_session, a_issue_id, a_project) send_and_wait_for_reply (l_msg) if is_ok then l_issue_reply_msg ?= last_reply check issue_reply_message: l_issue_reply_msg /= Void end create Result.make (7) create l_issue_id_ref l_issue_id_ref.set_item (a_issue_id) Result.force (l_issue_id_ref, "issue_id") create l_creation_time_ref l_creation_time_ref.set_item (l_issue_reply_msg.creation_time) Result.force (l_creation_time_ref, "creation_time") create l_last_modified_ref l_last_modified_ref.set_item (l_issue_reply_msg.last_modified) Result.force (l_last_modified_ref, "last_modified") create l_is_private_ref l_is_private_ref.set_item (l_issue_reply_msg.is_private) Result.force (l_is_private_ref, "is_private") Result.force (l_issue_reply_msg.user, "user") Result.force (l_issue_reply_msg.title, "title") Result.force (l_issue_reply_msg.tags, "tags") l_issue_revisions ?= l_issue_reply_msg.revisions check l_issue_revisions_valid: l_issue_revisions /= Void end create l_revision_count_ref l_revision_count_ref.set_item (l_issue_revisions.count) Result.force (l_revision_count_ref, "revision_count") from l_issue_revisions.start i := 1 until l_issue_revisions.after loop l_issue_revision ?= l_issue_revisions.item check issue_revision_valid: l_issue_revision /= Void end create l_rev_creation_time_ref l_rev_creation_time_ref.set_item (l_issue_revision.creation_time) Result.force (l_rev_creation_time_ref, "revision_creationtime_" + i.out) Result.force (l_issue_revision.user, "revision_user_" + i.out) Result.force (l_issue_revision.text, "revision_text_" + i.out) Result.force (l_issue_revision.tags, "revision_tags_" + i.out) l_issue_revisions.forth i := i + 1 end end end end issue_search (a_session: STRING; a_project: INTEGER_REF; a_search_criteria: DS_HASH_TABLE [ANY, STRING]): ARRAY [DS_HASH_TABLE [ANY, STRING]] is -- search issues in a_project according to a_search_criteria local l_msg: O_ISSUE_SEARCH_MESSAGE l_issue_list_reply_msg: O_ISSUE_LIST_REPLY_MESSAGE l_search_criteria: DS_HASH_TABLE [STRING, STRING] l_criterion_value: STRING l_ilist: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING], INTEGER] l_issue_t: TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING] l_issue: DS_HASH_TABLE [ANY, STRING] i: INTEGER l_issue_id_ref: INTEGER_REF l_creation_time_ref: INTEGER_REF l_last_modified_ref: INTEGER_REF do -- FIXME validate struct -- argument validiation check_anonymous_session (a_session) if not is_valid_session (a_session) then last_fault := err_invalid_session elseif a_project <= 0 then last_fault := err_invalid_project elseif a_search_criteria = Void then last_fault := err_invalid_search_criteria else -- convert DS_HASH_TABLE [ANY, STRING] to DS_HASH_TABLE [STRING, STRING] create l_search_criteria.make (a_search_criteria.count) from a_search_criteria.start until a_search_criteria.after loop l_criterion_value ?= a_search_criteria.item_for_iteration if l_criterion_value = Void then last_fault := err_invalid_search_criteria else l_search_criteria.force (l_criterion_value, a_search_criteria.key_for_iteration) end a_search_criteria.forth end create l_msg.make (a_session, a_project, l_search_criteria) send_and_wait_for_reply (l_msg) if is_ok then -- result conversion l_issue_list_reply_msg ?= last_reply check issue_list_reply_message: l_issue_list_reply_msg /= Void end -- TODO Use convert_issue_list for conversion l_ilist := l_issue_list_reply_msg.issue_list create Result.make (0, l_ilist.count-1) from i := 0 until i >= l_ilist.count loop l_issue_t := l_ilist.item (i) create l_issue.make (5) create l_issue_id_ref l_issue_id_ref.set_item (l_issue_t.issue_id) l_issue.force (l_issue_id_ref, "issue_id") create l_creation_time_ref l_creation_time_ref.set_item (l_issue_t.creation_time) l_issue.force (l_creation_time_ref, "creation_time") create l_last_modified_ref l_last_modified_ref.set_item (l_issue_t.last_modified) l_issue.force (l_last_modified_ref, "last_modified") l_issue.force (l_issue_t.user, "user") l_issue.force (l_issue_t.title, "title") l_issue.force (l_issue_t.tags, "tags") Result.force (l_issue, i) i := i + 1 end end end end feature -- Creation new_tuple (a_name: STRING): TUPLE is -- Tuple of default-valued arguments to pass to call `a_name'. do if a_name.is_equal (issue_add_name) then create {TUPLE [STRING, INTEGER_REF, STRING, STRING, STRING, BOOLEAN_REF]}Result elseif a_name.is_equal(issue_update_name) then create {TUPLE [STRING, INTEGER_REF, INTEGER_REF, STRING, STRING, STRING, BOOLEAN_REF]}Result elseif a_name.is_equal (issue_comment_name) then create {TUPLE [STRING, INTEGER_REF, INTEGER_REF, STRING]}Result elseif a_name.is_equal (issue_comment_extended_name) then create {TUPLE [STRING, INTEGER_REF, INTEGER_REF, STRING, STRING]}Result elseif a_name.is_equal (issue_list_name) then create {TUPLE [STRING, INTEGER_REF]}Result elseif a_name.is_equal (issue_retrieve_name) then create {TUPLE [STRING, INTEGER_REF, INTEGER_REF]}Result elseif a_name.is_equal (issue_search_name) then create {TUPLE [STRING, INTEGER_REF, DS_HASH_TABLE [ANY, STRING]]}Result elseif a_name.is_equal (issue_list_tags_name) then create {TUPLE [STRING, INTEGER_REF]}Result elseif a_name.is_equal (issue_list_changed_name) then create {TUPLE [STRING, INTEGER_REF, INTEGER_REF]}Result end end feature -- Initialisation self_register is -- Register all actions for this service do register_with_help (agent issue_add, issue_add_name, "Add issue.") register_with_help (agent issue_update, issue_update_name, "Update issue.") register_with_help (agent issue_comment, issue_comment_name, "Add issue reply.") register_with_help (agent issue_comment_extended, issue_comment_extended_name, "Add issue reply with new tags.") register_with_help (agent issue_list, issue_list_name, "List issues.") register_with_help (agent issue_list_tags, issue_list_tags_name, "List all tags used in a project.") register_with_help (agent issue_list_changed, issue_list_changed_name, "List all changed issues in a project.") register_with_help (agent issue_retrieve, issue_retrieve_name, "Retrieve an issue.") Register_with_help (agent issue_search, issue_search_name, "search issues.") end feature -- Helper convert_issue_list(a_issue_list: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING], INTEGER]): ARRAY [DS_HASH_TABLE [ANY, STRING]] is -- Converts a Tuple based representation of issues into an hash table based representation. -- This is used in the issue_list_* calls to convert O_ISSUE_LIST_REPLY_MESSAGE.issue_list local l_issue_t: TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING] l_issue: DS_HASH_TABLE [ANY, STRING] l_issue_id_ref: INTEGER_REF l_creation_time_ref: INTEGER_REF l_last_modified_ref: INTEGER_REF i: INTEGER do create Result.make (0, a_issue_list.count-1) from i := 0 until i >= a_issue_list.count loop l_issue_t := a_issue_list.item (i) create l_issue.make (5) create l_issue_id_ref l_issue_id_ref.set_item (l_issue_t.issue_id) l_issue.force (l_issue_id_ref, "issue_id") create l_creation_time_ref l_creation_time_ref.set_item (l_issue_t.creation_time) l_issue.force (l_creation_time_ref, "creation_time") create l_last_modified_ref l_last_modified_ref.set_item (l_issue_t.last_modified) l_issue.force (l_last_modified_ref, "last_modified") l_issue.force (l_issue_t.user, "user") l_issue.force (l_issue_t.title, "title") l_issue.force (l_issue_t.tags, "tags") Result.force (l_issue, i) i := i + 1 end end feature {INTERNAL_ISSUE_SERVICE} -- queries is_internal: BOOLEAN -- true if the operations of this service can only be called from the frontend -- The main reason for this flag was to minimize code duplication between this class -- and INTERNAL_ISSUE_SERVICE. feature {NONE} -- Validation valid_tags(a_tags: STRING): BOOLEAN is -- returns true if the tags are valid local l_tags: LIST [STRING] l_tag: STRING l_status_present: BOOLEAN do if a_tags = Void then -- if this occurs something went seriously wrong Result := False else l_tags := a_tags.split (',') if l_tags.is_empty then -- no tags specified Result := False else Result := True l_status_present := False from l_tags.start until l_tags.after loop l_tag := l_tags.item l_tag.right_adjust l_tag.left_adjust if l_tag.count <= 0 then -- empty tag Result := False elseif l_tag.is_equal ("status::open") or l_tag.is_equal ("status::closed") then if l_status_present then -- multiple status tags Result := False else l_status_present := True end end l_tags.forth end if not l_status_present then -- status tag is missing Result := False end end end end feature {NONE} -- Implementation issue_add_name: STRING is "add" issue_update_name: STRING is "update" issue_comment_name: STRING is "comment" issue_comment_extended_name: STRING is "comment_extended" issue_list_name: STRING is "list" issue_list_changed_name: STRING is "list_changed" issue_list_tags_name: STRING is "list_tags" issue_retrieve_name: STRING is "retrieve" issue_search_name: STRING is "search" end