def _iter_issue_batches(ids): """Generates a sequence of batches of issues from Issue Tracker by IDs.""" cli = issues.Client() for i in xrange(0, len(ids), _BATCH_SIZE): chunk = ids[i:i + _BATCH_SIZE] logger.debug('Issue ids to process: %s', chunk) try: response = cli.search({ 'issue_ids': chunk, 'page_size': _BATCH_SIZE, }) except integrations_errors.HttpError as error: logger.error('Unable to fetch Issue Tracker issues by IDs: %r', error) return issue_infos = {} response_issues = response.get('issues') or [] for info in response_issues: state = info['issueState'] or {} issue_infos[info['issueId']] = { 'status': state.get('status'), 'type': state.get('type'), 'priority': state.get('priority'), 'severity': state.get('severity'), } if issue_infos: yield issue_infos
def build_params_for_issue_link(self, obj, ticket_id, it_info): """Build update issue query for linking IssueTracker ticket to Issue""" allowed_emails = self._build_allowed_emails(obj) if allowed_emails is None: return self.params try: res = issues.Client().get_issue(ticket_id) except integrations_errors.Error as error: logger.error( "Unable to link a ticket while creating object ID=%d: %s", obj.id, error, ) obj.add_warning( "Ticket tracker ID does not exist or you do not have access to it." ) else: self.params.status = res["issueState"]["status"] self._add_link_message(obj) self.handle_issue_tracker_info(obj, it_info) self._populate_hotlist(it_info, res) self._handle_emails_from_response(res) self.params.reporter = obj.modified_by.email return self.params
def _handle_assessment_deleted(sender, obj=None, service=None): """Handles assessment delete event.""" del sender, service # Unused issue_obj = all_models.IssuetrackerIssue.get_issue( _ASSESSMENT_MODEL_NAME, obj.id) if issue_obj: if (issue_obj.enabled and issue_obj.issue_id and _is_issue_tracker_enabled(audit=obj.audit)): issue_params = { 'status': 'OBSOLETE', 'comment': ( 'Assessment has been deleted. Changes to this GGRC ' 'Assessment will no longer be tracked within this bug.' ), } try: issues.Client().update_issue(issue_obj.issue_id, issue_params) except integrations_errors.Error as error: logger.error('Unable to update a ticket ID=%s while deleting' ' assessment ID=%d: %s', issue_obj.issue_id, obj.id, error) db.session.delete(issue_obj)
def _create_issuetracker_issue(assessment, issue_tracker_info): """Collects information and sends a request to create external issue.""" _normalize_issue_tracker_info(issue_tracker_info) person, acl, acr = (all_models.Person, all_models.AccessControlList, all_models.AccessControlRole) reporter_email = db.session.query( person.email, ).join( acl, person.id == acl.person_id, ).join( acr, sa.and_( acl.ac_role_id == acr.id, acr.name == "Audit Captains", ), ).filter( acl.object_id == assessment.audit_id, acl.object_type == all_models.Audit.__name__, ).order_by( person.email, ).first() if reporter_email: reporter_email = reporter_email.email comment = [_INITIAL_COMMENT_TMPL % _get_assessment_url(assessment)] test_plan = assessment.test_plan if test_plan: comment.extend([ 'Following is the assessment Requirements/Assessment Procedure ' 'from GGRC:', html2text.HTML2Text().handle(test_plan).strip('\n'), ]) hotlist_id = issue_tracker_info.get('hotlist_id') issue_params = { 'component_id': issue_tracker_info['component_id'], 'hotlist_ids': [hotlist_id] if hotlist_id else [], 'title': issue_tracker_info['title'], 'type': issue_tracker_info['issue_type'], 'priority': issue_tracker_info['issue_priority'], 'severity': issue_tracker_info['issue_severity'], 'reporter': reporter_email, 'assignee': '', 'verifier': '', 'ccs': [], 'comment': '\n'.join(comment), } _update_issue_params_assignee(issue_params, issue_tracker_info) cc_list = issue_tracker_info.get('cc_list') if cc_list is not None: issue_params['ccs'] = cc_list res = issues.Client().create_issue(issue_params) return res['issueId']
def create_ticket_for_new_issue(obj, issue_tracker_info): """Create new IssueTracker ticket for issue""" builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_params = builder.build_create_issue_tracker_params( obj, issue_tracker_info) if issue_tracker_params.is_empty(): return # Query to IssueTracker. issue_tracker_query = issue_tracker_params.get_issue_tracker_params() # Parameters for creation IssuetrackerIssue object in GGRC. issuetracker_issue_params = issue_tracker_params.get_params_for_ggrc_object( ) try: res = issues.Client().create_issue(issue_tracker_query) issue_url = integration_utils.build_issue_tracker_url(res["issueId"]) issuetracker_issue_params["issue_url"] = issue_url issuetracker_issue_params["issue_id"] = res["issueId"] except integrations_errors.Error as error: logger.error( "Unable to create a ticket while creating object ID=%d: %s", obj.id, error) obj.add_warning("Unable to create a ticket in issue tracker.") issuetracker_issue_params["enabled"] = False # Create object in GGRC with info about issue tracker integration. all_models.IssuetrackerIssue.create_or_update_from_dict( obj, issuetracker_issue_params)
def sync_assessment_statuses(): """Synchronizes issue tracker ticket statuses with the Assessment statuses. Checks for Assessments which are in sync with Issue Tracker issues and updates their statuses in accordance to the corresponding Assessments if differ. """ assessment_issues = sync_utils.collect_issue_tracker_info("Assessment", include_ccs=True) if not assessment_issues: return logger.debug('Syncing state of %d issues.', len(assessment_issues)) cli = issues.Client() processed_ids = set() for batch in sync_utils.iter_issue_batches(assessment_issues.keys()): for issue_id, issuetracker_state in batch.iteritems(): issue_id = str(issue_id) issue_info = assessment_issues.get(issue_id) if not issue_info: logger.warning( 'Got an unexpected issue from Issue Tracker: %s', issue_id) continue processed_ids.add(issue_id) assessment_state = issue_info['state'] status_value = ASSESSMENT_STATUSES_MAPPING.get( assessment_state["status"]) if not status_value: logger.error( 'Inexistent Issue Tracker status for assessment ID=%d ' 'with status: %s.', issue_info['object_id'], status_value) continue assessment_state["status"] = status_value if all( assessment_state.get(field) == issuetracker_state.get( field) for field in FIELDS_TO_CHECK) and _compare_ccs( assessment_state.get("ccs", []), issuetracker_state.get("ccs", [])): continue try: sync_utils.update_issue(cli, issue_id, assessment_state) except integrations_errors.Error as error: logger.error( 'Unable to update status of Issue Tracker issue ID=%s for ' 'assessment ID=%d: %r', issue_id, issue_info['object_id'], error) logger.debug('Sync is done, %d issue(s) were processed.', len(processed_ids)) missing_ids = set(assessment_issues) - processed_ids if missing_ids: logger.warning( 'Some issues are linked to Assessments ' 'but were not found in Issue Tracker: %s', ', '.join(str(i) for i in missing_ids))
def _link_assessment(assessment, issue_tracker_info): """Link Assessment to existing IssueTracker ticket""" ticket_id = issue_tracker_info['issue_id'] if integration_utils.is_already_linked(ticket_id): logger.error( "Unable to link a ticket while creating object ID=%d: %s ticket ID is " "already linked to another GGRC object", assessment.id, ticket_id, ) assessment.add_warning( "This ticket was already linked to another GGRC issue, assessment or " "review object. Linking the same ticket to multiple objects is not " "allowed due to potential for conflicting updates.") return try: response = issues.Client().get_issue(ticket_id) except integrations_errors.Error as error: logger.error( "Unable to link a ticket while creating object ID=%d: %s", assessment.id, error, ) assessment.add_warning( "Ticket tracker ID does not exist or you do not have access to it." ) return issue_params = prepare_issue_json(assessment, issue_tracker_info, True) issuetracker_ccs = response.get("issueState", {}).get("ccs", []) grouped_ccs = group_cc_emails(object_ccs=issue_params.get("ccs", []), additional_ccs=issuetracker_ccs) issue_params["ccs"] = grouped_ccs try: issues.Client().update_issue(ticket_id, issue_params) except integrations_errors.Error as error: logger.error( 'Unable to link a ticket while creating assessment ID=%d: %s', assessment.id, error) issue_tracker_info['enabled'] = False assessment.add_warning('Unable to link a ticket.') else: issue_url = integration_utils.build_issue_tracker_url(ticket_id) issue_tracker_info['issue_url'] = issue_url all_models.IssuetrackerIssue.create_or_update_from_dict( assessment, issue_tracker_info)
def _update_issuetracker_issue(assessment, issue_tracker_info, initial_assessment, initial_info, request): """Collects information and sends a request to update external issue.""" # pylint: disable=too-many-locals issue_id = issue_tracker_info.get('issue_id') if not issue_id: return comments = [] # Handle switching of 'enabled' property. enabled = issue_tracker_info.get('enabled', False) if initial_info.get('enabled', False) != enabled: # Add comment about toggling feature and process further. comments.append(_ENABLED_TMPL if enabled else _DISABLED_TMPL) elif not enabled: # If feature remains in the same status which is 'disabled'. return integration_utils.normalize_issue_tracker_info(issue_tracker_info) issue_params = _handle_basic_props(issue_tracker_info, initial_info) # Handle status update. status_value, status_comment = _build_status_comment( assessment, initial_assessment) if status_value: issue_params['status'] = status_value comments.append(status_comment) # Attach user comments if any. comment_text, comment_author = _get_added_comment_text(request) if comment_text is not None: comments.append( _COMMENT_TMPL % (comment_author, comment_text, _get_assessment_url(assessment))) if comments: issue_params['comment'] = '\n\n'.join(comments) # Handle hotlist ID update. hotlist_id = issue_tracker_info.get('hotlist_id') if hotlist_id is not None and hotlist_id != initial_info.get('hotlist_id'): issue_params['hotlist_ids'] = [hotlist_id] if hotlist_id else [] # handle assignee and cc_list update assignee_email, cc_list = _collect_issue_emails(assessment) if assignee_email is not None: issue_tracker_info['assignee'] = assignee_email issue_params['assignee'] = assignee_email issue_params['verifier'] = assignee_email issue_params['ccs'] = cc_list if issue_params: # Resend all properties upon any change. issue_params = _fill_current_value(issue_params, assessment, initial_info) issues.Client().update_issue(issue_id, issue_params)
def test_get_issue_raises_exception(self, error_code, fetch_mock): """Test issues.Client raises appropriate exception for error codes""" fetch_mock.return_value = ObjectDict({ 'status_code': error_code, 'content': '{"status": "content"}' }) with mock.patch.multiple(self.testable_cls, ENDPOINT='endpoint'): self.assertRaises(integrations_errors.HttpError, issues.Client().get_issue, 'some_id')
def _detach_assessment(new_ticket_id, old_ticket_id): """Send to old IssueTracker ticket detachment comment.""" builder = issue_tracker_params_builder.AssessmentParamsBuilder() params = builder.build_detach_comment(new_ticket_id) query = params.get_issue_tracker_params() try: issues.Client().update_issue(old_ticket_id, query) except integrations_errors.Error as error: logger.error("Unable to add detach comment to ticket issue ID=%d: %s", old_ticket_id, error)
def link_issue(obj, ticket_id, issue_tracker_info): """Link issue to existing IssueTracker ticket""" if _is_already_linked(ticket_id): logger.error( "Unable to link a ticket while creating object ID=%d: %s ticket ID is " "already linked to another GGRC object", obj.id, ticket_id, ) obj.add_warning( "This ticket was already linked to another GGRC issue, assessment " "or review object. Linking the same ticket to multiple objects is not " "allowed due to potential for conflicting updates." ) return builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_container = builder.build_params_for_issue_link( obj, ticket_id, issue_tracker_info, ) if issue_tracker_container.is_empty(): return # Query to IssueTracker. issue_tracker_query = issue_tracker_container.get_issue_tracker_params() # Parameters for creation IssuetrackerIssue object in GGRC. issuetracker_issue_params = \ issue_tracker_container.get_params_for_ggrc_object() try: issues.Client().update_issue(ticket_id, issue_tracker_query) ticket_url = integration_utils.build_issue_tracker_url(ticket_id) issuetracker_issue_params["issue_url"] = ticket_url issuetracker_issue_params["issue_id"] = ticket_id update_initial_issue(obj, issue_tracker_container) except integrations_errors.Error as error: logger.error("Unable to update a ticket ID=%s while deleting" " issue ID=%d: %s", ticket_id, obj.id, error) obj.add_warning("Unable to update a ticket in issue tracker.") issuetracker_issue_params["enabled"] = False return if issuetracker_issue_params: all_models.IssuetrackerIssue.create_or_update_from_dict( obj, issuetracker_issue_params )
def _create_issuetracker_issue(assessment, issue_tracker_info): """Collects information and sends a request to create external issue.""" _normalize_issue_tracker_info(issue_tracker_info) reported_email = None reporter_id = get_current_user_id() if reporter_id: reporter = all_models.Person.query.filter( all_models.Person.id == reporter_id).first() if reporter is not None: reported_email = reporter.email comment = [ 'This bug was auto-generated to track a GGRC assessment ' '(a.k.a PBC Item). Use the following link to find the ' 'assessment - %s.' % _get_assessment_url(assessment), ] test_plan = assessment.test_plan if test_plan: comment.extend([ 'Following is the assessment Requirements/Assessment Procedure ' 'from GGRC:', html2text.HTML2Text().handle(test_plan).strip('\n'), ]) hotlist_id = issue_tracker_info.get('hotlist_id') issue_params = { 'component_id': issue_tracker_info['component_id'], 'hotlist_ids': [hotlist_id] if hotlist_id else [], 'title': issue_tracker_info['title'], 'type': issue_tracker_info['issue_type'], 'priority': issue_tracker_info['issue_priority'], 'severity': issue_tracker_info['issue_severity'], 'reporter': reported_email, 'assignee': '', 'verifier': '', 'ccs': [], 'comment': '\n'.join(comment), } assignee = issue_tracker_info.get('assignee') if assignee: issue_params['status'] = 'ASSIGNED' issue_params['assignee'] = assignee issue_params['verifier'] = assignee cc_list = issue_tracker_info.get('cc_list') if cc_list is not None: issue_params['ccs'] = cc_list res = issues.Client().create_issue(issue_params) return res['issueId']
def update_issue_handler(obj, initial_state, new_issue_tracker_info=None): """Event handler for issue object renewal.""" issue_tracker_object = all_models.IssuetrackerIssue.get_issue( "Issue", obj.id) if not issue_tracker_object: if new_issue_tracker_info and new_issue_tracker_info["enabled"]: create_issue_handler(obj, new_issue_tracker_info) return # Try to create ticket in Issue tracker if previous try failed. if new_issue_tracker_info and new_issue_tracker_info["enabled"] \ and not issue_tracker_object.issue_id: create_issue_handler(obj, new_issue_tracker_info) return current_issue_tracker_info = issue_tracker_object.to_dict( include_issue=True, include_private=True) if not new_issue_tracker_info: # Use existing issue tracker info if object is updating via import new_issue_tracker_info = current_issue_tracker_info # Build query builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_params = builder.build_update_issue_tracker_params( obj, initial_state, new_issue_tracker_info, current_issue_tracker_info) # Query to IssueTracker. issue_tracker_query = issue_tracker_params.get_issue_tracker_params() # Parameters for creation IssuetrackerIssue object in GGRC. issuetracker_issue_params = issue_tracker_params.get_params_for_ggrc_object( ) if not issue_tracker_params.is_empty(): try: issue_id = issue_tracker_object.issue_id issues.Client().update_issue(issue_id, issue_tracker_query) except integrations_errors.Error as error: logger.error( "Unable to update a ticket ID=%s while deleting" " issue ID=%d: %s", issue_tracker_object.issue_id, obj.id, error) obj.add_warning("Unable to update a ticket in issue tracker.") if issuetracker_issue_params: all_models.IssuetrackerIssue.create_or_update_from_dict( obj, issuetracker_issue_params)
def sync_issue_tracker_statuses(): """Synchronizes issue tracker ticket statuses with the Assessment statuses. Checks for Assessments which are in sync with Issue Tracker issues and updates their statuses in accordance to the corresponding Assessments if differ. """ assessment_issues = _collect_assessment_issues() if not assessment_issues: return logger.debug('Syncing state of %d issues.', len(assessment_issues)) cli = issues.Client() processed_ids = set() for batch in _iter_issue_batches(list(assessment_issues)): for issue_id, issuetracker_state in batch.iteritems(): issue_id = str(issue_id) issue_info = assessment_issues.get(issue_id) if not issue_info: logger.warning( 'Got an unexpected issue from Issue Tracker: %s', issue_id) continue processed_ids.add(issue_id) assessment_state = issue_info['state'] if all( assessment_state.get(field) == issuetracker_state.get( field) for field in _FIELDS_TO_CHECK): continue try: _update_issue(cli, issue_id, assessment_state) except integrations_errors.Error as error: logger.error( 'Unable to update status of Issue Tracker issue ID=%s for ' 'assessment ID=%d: %r', issue_id, issue_info['assessment_id'], error) logger.debug('Sync is done, %d issue(s) were processed.', len(processed_ids)) missing_ids = set(assessment_issues) - processed_ids if missing_ids: logger.warning( 'Some issues are linked to Assessments ' 'but were not found in Issue Tracker: %s', ', '.join(str(i) for i in missing_ids))
def _create_new_issuetracker_ticket(assessment, issue_tracker_info): """Create new IssueTracker ticket for assessment""" issue_tracker_request = prepare_issue_json(assessment, issue_tracker_info, create_issuetracker=True) try: res = issues.Client().create_issue(issue_tracker_request) except integrations_errors.Error as error: logger.error( 'Unable to create a ticket while creating assessment ID=%d: %s', assessment.id, error) issue_tracker_info['enabled'] = False assessment.add_warning('Unable to create a ticket.') else: issue_id = res['issueId'] issue_url = integration_utils.build_issue_tracker_url(issue_id) issue_tracker_info['issue_id'] = issue_id issue_tracker_info['issue_url'] = issue_url
def delete_issue_handler(obj, **kwargs): """Event handler for issue object deletion.""" issue_tracker_object = all_models.IssuetrackerIssue.get_issue("Issue", obj.id) if issue_tracker_object: if issue_tracker_object.enabled and issue_tracker_object.issue_id: builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_params = builder.build_delete_issue_tracker_params() issue_tracker_query = issue_tracker_params.get_issue_tracker_params() try: issues.Client().update_issue(issue_tracker_object.issue_id, issue_tracker_query) except integrations_errors.Error as error: logger.error("Unable to update a ticket ID=%s while deleting" " issue ID=%d: %s", issue_tracker_object.issue_id, obj.id, error) obj.add_warning("Unable to update a ticket in issue tracker.") db.session.delete(issue_tracker_object)
def create_comment_handler(sync_object, comment, author): """Event handler for adding comment to Issue object.""" issue_tracker_object = all_models.IssuetrackerIssue.get_issue( "Issue", sync_object.id) if not issue_tracker_object or not issue_tracker_object.enabled: return builder = issue_tracker_params_builder.IssueParamsBuilder() params = builder.build_params_for_comment(sync_object, comment.description, author) query = params.get_issue_tracker_params() try: issues.Client().update_issue(issue_tracker_object.issue_id, query) except integrations_errors.Error as error: logger.error("Unable to add comment to ticket issue ID=%d: %s", issue_tracker_object.issue_id, error) sync_object.add_warning("Unable to update a ticket in issue tracker.")
def update_audit_issues(args): """Web hook to update the issues associated with an audit.""" audit_id = args.parameters['audit_id'] message = args.parameters['message'] if not audit_id or not message: logger.warning( 'One or more of required parameters (audit_id, message) is empty.') return app.make_response(( 'Parameters audit_id and message are required.', 400, [('Content-Type', 'text/html')])) issue_tracker = models.all_models.IssuetrackerIssue relationships = models.all_models.Relationship query = db.session.query( issue_tracker.enabled, issue_tracker.issue_id, issue_tracker.object_id ).join( relationships, relationships.source_id == issue_tracker.object_id ).filter( relationships.source_type == 'Assessment', relationships.destination_type == 'Audit', relationships.destination_id == audit_id ) cli = issues.Client() issue_params = { 'comment': message, } for enabled, issue_id, assessment_id in query.all(): if not enabled: continue try: cli.update_issue(issue_id, issue_params) except integrations_errors.Error as error: logger.error( 'Unable to update IssueTracker issue ID=%s ' 'for Assessment ID=%s while archiving/unarchiving Audit ID=%s: %s', issue_id, assessment_id, audit_id, error) return app.make_response(('success', 200, [('Content-Type', 'text/html')]))
def iter_issue_batches(ids, batch_size=_BATCH_SIZE): """Generates a sequence of batches of issues from Issue Tracker by IDs.""" cli = issues.Client() for i in xrange(0, len(ids), batch_size): chunk = ids[i:i + batch_size] logger.debug('Issue ids to process: %s', chunk) try: response = cli.search({ 'issue_ids': chunk, 'page_size': batch_size, }) except integrations_errors.HttpError as error: logger.error('Unable to fetch Issue Tracker issues by IDs: %r', error) return issue_infos = {} response_issues = response.get('issues') or [] for info in response_issues: state = info['issueState'] or {} issue_info = { 'status': state.get('status'), 'type': state.get('type'), 'priority': state.get('priority'), 'severity': state.get('severity'), 'custom_fields': state.get('custom_fields', []), 'ccs': state.get('ccs', []), 'assignee': state.get('assignee'), 'reporter': state.get('reporter'), 'verifier': state.get('verifier'), 'hotlist_ids': state.get('hotlist_ids'), 'component_id': state.get('component_id') } issue_infos[info['issueId']] = issue_info if issue_infos: yield issue_infos
def create_issue_handler(obj, issue_tracker_info): """Event handler for issue object creation.""" if not issue_tracker_info or not issue_tracker_info.get("enabled"): return # We need in flush here because we need object id for URL generation. db.session.flush() builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_params = builder.build_create_issue_tracker_params( obj, issue_tracker_info) if issue_tracker_params.is_empty(): return # Query to IssueTracker. issue_tracker_query = issue_tracker_params.get_issue_tracker_params() # Parameters for creation IssuetrackerIssue object in GGRC. issuetracker_issue_params = issue_tracker_params.get_params_for_ggrc_object( ) try: res = issues.Client().create_issue(issue_tracker_query) issue_url = integration_utils.build_issue_tracker_url(res["issueId"]) issuetracker_issue_params["issue_url"] = issue_url issuetracker_issue_params["issue_id"] = res["issueId"] except integrations_errors.Error as error: logger.error( "Unable to create a ticket while creating object ID=%d: %s", obj.id, error) obj.add_warning("Unable to create a ticket in issue tracker.") issuetracker_issue_params["enabled"] = False # Create object in GGRC with info about issue tracker integration. all_models.IssuetrackerIssue.create_or_update_from_dict( obj, issuetracker_issue_params)
def sync_assessment_attributes(): # noqa """Synchronizes issue tracker ticket statuses with the Assessment statuses. Checks for Assessments which are in sync with Issue Tracker issues and updates their statuses in accordance to the corresponding Assessments if differ. """ assessment_issues = sync_utils.collect_issue_tracker_info("Assessment", include_ccs=True) if not assessment_issues: return logger.debug("Syncing state of %d issues.", len(assessment_issues)) cli = issues.Client() processed_ids = set() for batch in sync_utils.iter_issue_batches(assessment_issues.keys()): for issue_id, issuetracker_state in batch.iteritems(): issue_id, issue_info = _get_issue_info_by_issue_id( issue_id, assessment_issues) if not issue_info: logger.warning( "Got an unexpected issue from Issue Tracker: %s", issue_id) continue object_id = issue_info["object_id"] processed_ids.add(issue_id) issue_payload = _prepare_issue_payload(issue_info) if not _is_need_synchronize_issue(object_id, issue_payload, issuetracker_state): continue _update_issue(cli, issue_id, object_id, issue_payload) logger.debug("Sync is done, %d issue(s) were processed.", len(processed_ids)) _check_missing_ids(assessment_issues, processed_ids)
def sync_issue_tracker_statuses(): """Synchronize issue tracker ticket statuses with the Assessment statuses. Check for Assessments which are in sync with issue tracker issues and update their statuses in accordance to the corresponding Assessments if differ. """ issue_objects = IssuetrackerIssue.query.filter( IssuetrackerIssue.object_type == 'Assessment', IssuetrackerIssue.enabled == expression.true(), IssuetrackerIssue.issue_id.isnot(None), ).order_by(IssuetrackerIssue.object_id).all() for iti in issue_objects: asmt = iti.issue_tracked_obj if not asmt: logger.error( "The Assessment corresponding to the Issue Tracker Issue ID=%d " "does not exist.", iti.issue_id) continue status_value = issues.STATUSES.get(asmt.status) if status_value: issue_params = { 'status': status_value, 'type': iti.issue_type, 'priority': iti.issue_priority, 'severity': iti.issue_severity, } else: logger.error( "Inexistent Issue Tracker status for assessment ID=%d " "with status: %s.", asmt.id, status_value) try: issues.Client().update_issue(iti.issue_id, issue_params) except integrations_errors.Error as error: logger.error( 'Unable to update IssueTracker issue status ID=%s ' 'for assessment ID=%d: %s', iti.issue_id, asmt.id, error)
def _update_issuetracker_issue( assessment, issue_tracker_info, # noqa initial_assessment, initial_info, request): """Collects information and sends a request to update external issue.""" # pylint: disable=too-many-locals issue_id = issue_tracker_info.get('issue_id') if not issue_id: return comments = [] # Handle switching of 'enabled' property. enabled = issue_tracker_info.get('enabled', False) if initial_info.get('enabled', False) != enabled: # Add comment about toggling feature and process further. comments.append(_ENABLED_TMPL if enabled else _DISABLED_TMPL) elif not enabled: # If feature remains in the same status which is 'disabled'. return integration_utils.normalize_issue_tracker_info(issue_tracker_info) issue_params = _handle_basic_props(issue_tracker_info, initial_info) # Handle status update. status_value, status_comment = _build_status_comment( assessment, initial_assessment) if status_value: issue_params['status'] = status_value comments.append(status_comment) # Attach user comments if any. comment_text, comment_author = _get_added_comment_text(request) if comment_text is not None: builder = issue_tracker_params_builder.AssessmentParamsBuilder() comments.append( builder.COMMENT_TMPL.format(author=comment_author, comment=comment_text, model=_ASSESSMENT_MODEL_NAME, link=_get_assessment_url(assessment))) if comments: issue_params['comment'] = '\n\n'.join(comments) # Handle hotlist ID update. hotlist_id = issue_tracker_info.get('hotlist_id') if hotlist_id is not None and hotlist_id != initial_info.get('hotlist_id'): issue_params['hotlist_ids'] = [hotlist_id] if hotlist_id else [] # handle assignee and cc_list update assignee_email, cc_list = _collect_assessment_emails(assessment) del cc_list if assignee_email is not None: issue_tracker_info['assignee'] = assignee_email issue_params['assignee'] = assignee_email issue_params['verifier'] = assignee_email custom_fields = [] # handle due_date update due_date = issue_tracker_info.get('due_date') if due_date: custom_fields.append({ "name": constants.CUSTOM_FIELDS_DUE_DATE, "value": due_date.strftime("%Y-%m-%d"), "type": "DATE", "display_string": constants.CUSTOM_FIELDS_DUE_DATE }) if custom_fields: issue_params['custom_fields'] = custom_fields if issue_params: # Resend all properties upon any change. issue_params = _fill_current_value(issue_params, assessment, initial_info) issues.Client().update_issue(issue_id, issue_params)
def update_issue_handler(obj, initial_state, new_issuetracker_info=None): # noqa """Event handler for issue object renewal.""" # TODO: refactor this handler to be not so complex and more generic if not new_issuetracker_info: return it_object = all_models.IssuetrackerIssue.get_issue("Issue", obj.id) old_ticket_id = None if it_object: old_ticket_id = int(it_object.issue_id) if it_object.issue_id else None get_id = new_issuetracker_info.get("issue_id") if new_issuetracker_info \ else None new_ticket_id = int(get_id) if get_id else None # We should create new ticket if new ticket_id is empty, we don't store # IssueTrackerIssue object or it contains empty ticket_id needs_creation = (not it_object) or \ (not old_ticket_id) or (not new_ticket_id) if needs_creation: create_issue_handler(obj, new_issuetracker_info) if not obj.warnings: it_issue = all_models.IssuetrackerIssue.get_issue( obj.__class__.__name__, obj.id) new_ticket_id = it_issue.issue_id if it_issue else None if old_ticket_id and new_ticket_id and old_ticket_id != new_ticket_id: detach_issue(new_ticket_id, old_ticket_id) return if not it_object: return if (new_ticket_id != old_ticket_id) and new_issuetracker_info["enabled"]: link_issue(obj, new_ticket_id, new_issuetracker_info) if not obj.warnings: detach_issue(new_ticket_id, old_ticket_id) return current_issue_tracker_info = it_object.to_dict(include_issue=True, include_private=True) # Build query builder = issue_tracker_params_builder.IssueParamsBuilder() issue_tracker_params = builder.build_update_issue_tracker_params( obj, initial_state, new_issuetracker_info, current_issue_tracker_info) # Query to IssueTracker. issue_tracker_query = issue_tracker_params.get_issue_tracker_params() # Parameters for creation IssuetrackerIssue object in GGRC. issuetracker_issue_params = issue_tracker_params.get_params_for_ggrc_object( ) if not issue_tracker_params.is_empty(): try: issue_id = it_object.issue_id issues.Client().update_issue(issue_id, issue_tracker_query) except integrations_errors.Error as error: logger.error( "Unable to update a ticket ID=%s while deleting" " issue ID=%d: %s", it_object.issue_id, obj.id, error) obj.add_warning("Unable to update a ticket in issue tracker.") if issuetracker_issue_params: all_models.IssuetrackerIssue.create_or_update_from_dict( obj, issuetracker_issue_params)
def __init__(self): self.break_on_errs = False self.client = issues.Client()
def _create_issuetracker_issue(assessment, issue_tracker_info): """Collects information and sends a request to create external issue.""" issue_params = prepare_issue_json(assessment, issue_tracker_info) res = issues.Client().create_issue(issue_params) return res['issueId']