diff --git a/grimoire_elk/enriched/gitlabcomments.py b/grimoire_elk/enriched/gitlabcomments.py new file mode 100644 index 000000000..543858bb5 --- /dev/null +++ b/grimoire_elk/enriched/gitlabcomments.py @@ -0,0 +1,573 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015-2020 Bitergia +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authors: +# Venu Vardhan Reddy Tekula +# + + +import logging +import re +import collections + +from grimoirelab_toolkit.datetime import str_to_datetime, datetime_utcnow + +from .utils import get_time_diff_days + +from .enrich import Enrich, metadata +from ..elastic_mapping import Mapping as BaseMapping + +MAX_SIZE_BULK_ENRICHED_ITEMS = 200 + +GITLAB = 'https://gitlab.com/' + +ISSUE_TYPE = 'issue' +MERGE_TYPE = 'merge_request' + +COMMENT_TYPE = 'comment' +ISSUE_COMMENT_TYPE = 'issue_comment' +MERGE_COMMENT_TYPE = 'merge_comment' + +logger = logging.getLogger(__name__) + + +class Mapping(BaseMapping): + + @staticmethod + def get_elastic_mappings(es_major): + """Get Elasticsearch mapping. + + :param es_major: major version of Elasticsearch, as string + :returns: dictionary with a key, 'items', with the mapping + """ + + mapping = """ + { + "properties": { + "title_analyzed": { + "type": "text", + "index": true + }, + "body_analyzed": { + "type": "text", + "index": true + }, + "id": { + "type": "keyword" + } + } + } + """ + + return {"items": mapping} + + +class GitLabCommentsEnrich(Enrich): + mapping = Mapping + + comment_roles = ['author'] + issue_roles = ['author', 'assignee'] + mr_roles = ['author', 'merged_by'] + roles = ['author', 'assignee', 'merged_by'] + + def __init__(self, db_sortinghat=None, db_projects_map=None, json_projects_map=None, + db_user='', db_password='', db_host=''): + super().__init__(db_sortinghat, db_projects_map, json_projects_map, + db_user, db_password, db_host) + + self.studies = [] + + def set_elastic(self, elastic): + self.elastic = elastic + + def get_field_author(self): + return "author" + + def get_field_date(self): + """ Field with the date in the JSON enriched items """ + return "grimoire_creation_date" + + def get_field_unique_id(self): + return "id" + + def add_gelk_metadata(self, eitem): + eitem['metadata__gelk_version'] = self.gelk_version + eitem['metadata__gelk_backend_name'] = self.__class__.__name__ + eitem['metadata__enriched_on'] = datetime_utcnow().isoformat() + + def get_identities(self, item): + """Return the identities from an item""" + + category = item['category'] + + item = item['data'] + comments_attr = None + if category == "issue": + identity_types = ['author', 'assignee'] + comments_attr = 'notes_data' + elif category == "merge_request": + identity_types = ['author', 'merged_by'] + comments_attr = 'notes_data' + else: + identity_types = [] + + for identity in identity_types: + if item[identity]: + user = self.get_sh_identity(item[identity]) + if user: + yield user + + comments = item.get(comments_attr, []) + for comment in comments: + user = self.get_sh_identity(comment['author']) + if user: + yield user + + def get_sh_identity(self, item, identity_field=None): + identity = {} + + user = item # by default a specific user dict is expected + if isinstance(item, dict) and 'data' in item: + user = item['data'][identity_field] + elif identity_field: + user = item[identity_field] + + if not user: + return identity + + identity['username'] = user.get('username', None) + identity['name'] = user.get('name', None) + identity['email'] = user.get('email', None) + + return identity + + def get_project_repository(self, eitem): + repo = eitem['origin'] + return repo + + def get_time_to_first_attention(self, item): + """Get the first date at which a comment or reaction was made to the issue by someone + other than the user who created the issue + """ + comments_dates = [str_to_datetime(comment['created_at']) for comment in item['notes_data'] + if item['author']['username'] != comment['author']['username']] + reaction_dates = [str_to_datetime(reaction['created_at']) for reaction in item['award_emoji_data'] + if item['author']['username'] != reaction['user']['username']] + reaction_dates.extend(comments_dates) + + if reaction_dates: + return min(reaction_dates) + + return None + + def get_time_to_merge_request_response(self, item): + """Get the first date at which a review was made on the MR by someone + other than the user who created the MR + """ + review_dates = [] + + for comment in item['notes_data']: + # skip comments of the merge request creator + if item['author']['username'] == comment['author']['username']: + continue + + review_dates.append(str_to_datetime(comment['created_at'])) + + if review_dates: + return min(review_dates) + + return None + + def __get_reactions(self, item): + item_reactions = item.get('award_emoji_data', []) + reactions_total_count = len(item_reactions) + if item_reactions: + reactions_counter = collections.Counter([reaction["name"] for reaction in item_reactions]) + item_reactions = [{"type": reaction, "count": reactions_counter[reaction]} for reaction in + reactions_counter] + + return {"reactions": item_reactions, "reactions_total_count": reactions_total_count} + + def __get_rich_issue(self, item): + rich_issue = {} + + self.copy_raw_fields(self.RAW_FIELDS_COPY, item, rich_issue) + + # the real data + issue = item['data'] + + rich_issue['time_to_close_days'] = \ + get_time_diff_days(issue['created_at'], issue['closed_at']) + + if issue['state'] != 'closed': + rich_issue['time_open_days'] = \ + get_time_diff_days(issue['created_at'], datetime_utcnow().replace(tzinfo=None)) + else: + rich_issue['time_open_days'] = rich_issue['time_to_close_days'] + + author = issue.get('author', None) + if author is not None and author: + rich_issue['author_login'] = author['username'] + rich_issue['author_name'] = author['name'] + rich_issue['author_domain'] = self.get_email_domain(author['email']) if author.get('email') else None + else: + rich_issue['author_login'] = None + rich_issue['author_name'] = None + rich_issue['author_domain'] = None + + assignee = issue.get('assignee', None) + if assignee is not None and assignee: + rich_issue['assignee_login'] = assignee['username'] + rich_issue['assignee_name'] = assignee['name'] + rich_issue['assignee_domain'] = self.get_email_domain(assignee['email']) if assignee.get('email') else None + else: + rich_issue['assignee_login'] = None + rich_issue['assignee_name'] = None + rich_issue['assignee_domain'] = None + + rich_issue['id'] = str(issue['id']) + rich_issue['issue_id'] = issue['id'] + rich_issue['issue_id_in_repo'] = issue['web_url'].split("/")[-1] + rich_issue['repository'] = self.get_project_repository(rich_issue) + rich_issue['issue_title'] = issue['title'] + rich_issue['issue_title_analyzed'] = issue['title'] + rich_issue['issue_state'] = issue['state'] + rich_issue['issue_created_at'] = issue['created_at'] + rich_issue['issue_updated_at'] = issue['updated_at'] + rich_issue['issue_closed_at'] = issue['closed_at'] + rich_issue['url'] = issue['web_url'] + rich_issue['issue_url'] = issue['web_url'] + + # extract reactions and add it to enriched item + rich_issue.update(self.__get_reactions(issue)) + + rich_issue['issue_labels'] = issue['labels'] + rich_issue['item_type'] = ISSUE_TYPE + + rich_issue['gitlab_repo'] = rich_issue['repository'].replace(GITLAB, '') + rich_issue['gitlab_repo'] = re.sub('.git$', '', rich_issue['gitlab_repo']) + + if self.prjs_map: + rich_issue.update(self.get_item_project(rich_issue)) + + if 'project' in item: + rich_issue['project'] = item['project'] + + rich_issue['time_to_first_attention'] = None + if len(issue['award_emoji_data']) + len(issue['notes_data']) != 0: + rich_issue['time_to_first_attention'] = \ + get_time_diff_days(str_to_datetime(issue['created_at']), + self.get_time_to_first_attention(issue)) + + rich_issue.update(self.get_grimoire_fields(issue['created_at'], ISSUE_TYPE)) + + # due to backtrack compatibility, `is_gitlabcomments_*` is replaced with `is_gitlab_*` + rich_issue.pop('is_gitlabcomments_{}'.format(ISSUE_TYPE)) + rich_issue['is_gitlab_{}'.format(ISSUE_TYPE)] = 1 + + if self.sortinghat: + item[self.get_field_date()] = rich_issue[self.get_field_date()] + rich_issue.update(self.get_item_sh(item, self.issue_roles)) + + return rich_issue + + def __get_rich_merge(self, item): + rich_mr = {} + + self.copy_raw_fields(self.RAW_FIELDS_COPY, item, rich_mr) + # The real data + merge_request = item['data'] + + rich_mr['time_to_close_days'] = \ + get_time_diff_days(merge_request['created_at'], merge_request['closed_at']) + + if merge_request['state'] != 'closed': + rich_mr['time_open_days'] = \ + get_time_diff_days(merge_request['created_at'], datetime_utcnow().replace(tzinfo=None)) + else: + rich_mr['time_open_days'] = rich_mr['time_to_close_days'] + + author = merge_request.get('author', None) + if author is not None and author: + rich_mr['author_login'] = author['username'] + rich_mr['author_name'] = author['name'] + rich_mr['author_domain'] = self.get_email_domain(author['email']) if author.get('email') else None + else: + rich_mr['author_login'] = None + rich_mr['author_name'] = None + rich_mr['author_domain'] = None + + merged_by = merge_request.get('merged_by', None) + if merged_by is not None and merged_by: + rich_mr['merge_author_login'] = merged_by['username'] + rich_mr['merge_author_name'] = merged_by['name'] + rich_mr['merge_author_domain'] = \ + self.get_email_domain(merged_by['email']) if merged_by.get('email') else None + else: + rich_mr['merge_author_login'] = None + rich_mr['merge_author_name'] = None + rich_mr['merge_author_domain'] = None + + rich_mr['id'] = str(merge_request['id']) + rich_mr['merge_id'] = merge_request['id'] + rich_mr['merge_id_in_repo'] = merge_request['web_url'].split("/")[-1] + rich_mr['repository'] = self.get_project_repository(rich_mr) + rich_mr['merge_title'] = merge_request['title'] + rich_mr['merge_title_analyzed'] = merge_request['title'] + rich_mr['merge_state'] = merge_request['state'] + rich_mr['merge_created_at'] = merge_request['created_at'] + rich_mr['merge_updated_at'] = merge_request['updated_at'] + rich_mr['merge_status'] = merge_request['merge_status'] + rich_mr['merge_merged_at'] = merge_request['merged_at'] + rich_mr['merge_closed_at'] = merge_request['closed_at'] + rich_mr['url'] = merge_request['web_url'] + rich_mr['merge_url'] = merge_request['web_url'] + + # extract reactions and add it to enriched item + rich_mr.update(self.__get_reactions(merge_request)) + + rich_mr['merge_labels'] = merge_request['labels'] + rich_mr['item_type'] = MERGE_TYPE + + rich_mr['gitlab_repo'] = rich_mr['repository'].replace(GITLAB, '') + rich_mr['gitlab_repo'] = re.sub('.git$', '', rich_mr['gitlab_repo']) + + # GMD code development metrics + rich_mr['code_merge_duration'] = get_time_diff_days(merge_request['created_at'], + merge_request['merged_at']) + rich_mr['num_versions'] = len(merge_request['versions_data']) + rich_mr['num_merge_comments'] = len(merge_request['notes_data']) + + rich_mr['time_to_merge_request_response'] = None + if merge_request['notes_data'] != 0: + min_review_date = self.get_time_to_merge_request_response(merge_request) + rich_mr['time_to_merge_request_response'] = \ + get_time_diff_days(str_to_datetime(merge_request['created_at']), min_review_date) + + if self.prjs_map: + rich_mr.update(self.get_item_project(rich_mr)) + + if 'project' in item: + rich_mr['project'] = item['project'] + + rich_mr.update(self.get_grimoire_fields(merge_request['created_at'], MERGE_TYPE)) + + # due to backtrack compatibility, `is_gitlabcomments_*` is replaced with `is_gitlab_*` + rich_mr.pop('is_gitlabcomments_{}'.format(MERGE_TYPE)) + rich_mr['is_gitlab_{}'.format(MERGE_TYPE)] = 1 + + if self.sortinghat: + item[self.get_field_date()] = rich_mr[self.get_field_date()] + rich_mr.update(self.get_item_sh(item, self.mr_roles)) + + return rich_mr + + @metadata + def get_rich_item(self, item): + rich_item = {} + + if item['category'] == 'issue': + rich_item = self.__get_rich_issue(item) + elif item['category'] == 'merge_request': + rich_item = self.__get_rich_merge(item) + else: + logger.error("[gitlab] rich item not defined for GitLab category {}".format( + item['category'])) + + self.add_repository_labels(rich_item) + self.add_metadata_filter_raw(rich_item) + + return rich_item + + def get_rich_issue_comments(self, comments, eitem): + ecomments = [] + + for comment in comments: + ecomment = {} + + self.copy_raw_fields(self.RAW_FIELDS_COPY, eitem, ecomment) + + # Copy data from the enriched issue + ecomment['issue_labels'] = eitem['issue_labels'] + ecomment['issue_id'] = eitem['issue_id'] + ecomment['issue_id_in_repo'] = eitem['issue_id_in_repo'] + ecomment['issue_url'] = eitem['issue_url'] + ecomment['issue_title'] = eitem['issue_title'] + ecomment['issue_state'] = eitem['issue_state'] + ecomment['issue_created_at'] = eitem['issue_created_at'] + ecomment['issue_updated_at'] = eitem['issue_updated_at'] + ecomment['issue_closed_at'] = eitem['issue_closed_at'] + ecomment['gitlab_repo'] = eitem['gitlab_repo'] + ecomment['repository'] = eitem['repository'] + ecomment['item_type'] = COMMENT_TYPE + ecomment['sub_type'] = ISSUE_COMMENT_TYPE + + # Copy data from the raw comment + ecomment['body'] = comment['body'][:self.KEYWORD_MAX_LENGTH] + ecomment['body_analyzed'] = comment['body'] + + ecomment['url'] = '{}#note_{}'.format(eitem['issue_url'], comment['id']) + + # extract reactions and add it to enriched item + ecomment.update(self.__get_reactions(comment)) + + ecomment['comment_updated_at'] = comment['updated_at'] + + # Add id info to allow to coexistence of items of different types in the same index + ecomment['id'] = '{}_issue_comment_{}'.format(eitem['id'], comment['id']) + ecomment.update(self.get_grimoire_fields(comment['updated_at'], ISSUE_COMMENT_TYPE)) + + # due to backtrack compatibility, `is_gitlabcomments_*` is replaced with `is_gitlab_*` + ecomment.pop('is_gitlabcomments_{}'.format(ISSUE_COMMENT_TYPE)) + ecomment['is_gitlab_{}'.format(ISSUE_COMMENT_TYPE)] = 1 + ecomment['is_gitlab_comment'] = 1 + + if self.sortinghat: + ecomment.update(self.get_item_sh(comment, self.comment_roles, 'updated_at')) + + if self.prjs_map: + ecomment.update(self.get_item_project(ecomment)) + + if 'project' in eitem: + ecomment['project'] = eitem['project'] + + self.add_repository_labels(ecomment) + self.add_metadata_filter_raw(ecomment) + self.add_gelk_metadata(ecomment) + + ecomments.append(ecomment) + + return ecomments + + def get_rich_merge_reviews(self, comments, eitem): + ecomments = [] + + for comment in comments: + ecomment = {} + + self.copy_raw_fields(self.RAW_FIELDS_COPY, eitem, ecomment) + + # Copy data from the enriched merge request + ecomment['merge_labels'] = eitem['merge_labels'] + ecomment['merge_id'] = eitem['merge_id'] + ecomment['merge_id_in_repo'] = eitem['merge_id_in_repo'] + ecomment['merge_title'] = eitem['merge_title'] + ecomment['merge_url'] = eitem['merge_url'] + ecomment['merge_state'] = eitem['merge_state'] + ecomment['merge_created_at'] = eitem['merge_created_at'] + ecomment['merge_updated_at'] = eitem['merge_updated_at'] + ecomment['merge_merged_at'] = eitem['merge_merged_at'] + ecomment['merge_closed_at'] = eitem['merge_closed_at'] + ecomment['merge_status'] = eitem['merge_status'] + ecomment['gitlab_repo'] = eitem['gitlab_repo'] + ecomment['repository'] = eitem['repository'] + ecomment['item_type'] = COMMENT_TYPE + ecomment['sub_type'] = MERGE_COMMENT_TYPE + + # Copy data from the raw comment + ecomment['body'] = comment['body'][:self.KEYWORD_MAX_LENGTH] + ecomment['body_analyzed'] = comment['body'] + + ecomment['url'] = '{}#note_{}'.format(eitem['merge_url'], comment['id']) + + # extract reactions and add it to enriched item + ecomment.update(self.__get_reactions(comment)) + + ecomment['comment_updated_at'] = comment['updated_at'] + + # Add id info to allow to coexistence of items of different types in the same index + ecomment['id'] = '{}_merge_comment_{}'.format(eitem['id'], comment['id']) + ecomment.update(self.get_grimoire_fields(comment['updated_at'], MERGE_COMMENT_TYPE)) + + # due to backtrack compatibility, `is_gitlabcomments_*` is replaced with `is_gitlab_*` + ecomment.pop('is_gitlabcomments_{}'.format(MERGE_COMMENT_TYPE)) + ecomment['is_gitlab_{}'.format(MERGE_COMMENT_TYPE)] = 1 + ecomment['is_gitlab_comment'] = 1 + + if self.sortinghat: + ecomment.update(self.get_item_sh(comment, self.comment_roles, 'updated_at')) + + if self.prjs_map: + ecomment.update(self.get_item_project(ecomment)) + + if 'project' in eitem: + ecomment['project'] = eitem['project'] + + self.add_repository_labels(ecomment) + self.add_metadata_filter_raw(ecomment) + self.add_gelk_metadata(ecomment) + + ecomments.append(ecomment) + + return ecomments + + def enrich_issue(self, item, eitem): + eitems = [] + + comments = item['data'].get('notes_data', []) + if comments: + rich_item_comments = self.get_rich_issue_comments(comments, eitem) + eitems.extend(rich_item_comments) + + return eitems + + def enrich_merge(self, item, eitem): + eitems = [] + + comments = item['data'].get('notes_data', []) + if comments: + rich_item_comments = self.get_rich_merge_reviews(comments, eitem) + eitems.extend(rich_item_comments) + + return eitems + + def enrich_items(self, ocean_backend): + items_to_enrich = [] + num_items = 0 + ins_items = 0 + + for item in ocean_backend.fetch(): + eitems = [] + + eitem = self.get_rich_item(item) + items_to_enrich.append(eitem) + + if item['category'] == ISSUE_TYPE: + eitems = self.enrich_issue(item, eitem) + elif item['category'] == MERGE_TYPE: + eitems = self.enrich_merge(item, eitem) + + items_to_enrich.extend(eitems) + + if len(items_to_enrich) < MAX_SIZE_BULK_ENRICHED_ITEMS: + continue + + num_items += len(items_to_enrich) + ins_items += self.elastic.bulk_upload(items_to_enrich, self.get_field_unique_id()) + items_to_enrich = [] + + if len(items_to_enrich) > 0: + num_items += len(items_to_enrich) + ins_items += self.elastic.bulk_upload(items_to_enrich, self.get_field_unique_id()) + + if num_items != ins_items: + missing = num_items - ins_items + logger.error("%s/%s missing items for GitLab", str(missing), str(num_items)) + else: + logger.info("%s items inserted for GitLab", str(num_items)) + + return num_items diff --git a/grimoire_elk/utils.py b/grimoire_elk/utils.py index d9c0069a5..1c6774583 100755 --- a/grimoire_elk/utils.py +++ b/grimoire_elk/utils.py @@ -95,6 +95,7 @@ from .enriched.githubql import GitHubQLEnrich from .enriched.github2 import GitHubEnrich2 from .enriched.gitlab import GitLabEnrich +from .enriched.gitlabcomments import GitLabCommentsEnrich from .enriched.gitter import GitterEnrich from .enriched.google_hits import GoogleHitsEnrich from .enriched.groupsio import GroupsioEnrich @@ -240,6 +241,7 @@ def get_connectors(): "githubql": [GitHubQL, GitHubQLOcean, GitHubQLEnrich, GitHubQLCommand], "github2": [GitHub, GitHubOcean, GitHubEnrich2, GitHubCommand], "gitlab": [GitLab, GitLabOcean, GitLabEnrich, GitLabCommand], + "gitlabcomments": [GitLab, GitLabOcean, GitLabCommentsEnrich, GitLabCommand], "gitter": [Gitter, GitterOcean, GitterEnrich, GitterCommand], "google_hits": [GoogleHits, GoogleHitsOcean, GoogleHitsEnrich, GoogleHitsCommand], "groupsio": [Groupsio, GroupsioOcean, GroupsioEnrich, GroupsioCommand], diff --git a/schema/gitlabcomments_issues.csv b/schema/gitlabcomments_issues.csv new file mode 100644 index 000000000..9578d2f10 --- /dev/null +++ b/schema/gitlabcomments_issues.csv @@ -0,0 +1,63 @@ +name,type,aggregatable,description +assignee_bot,boolean,true,"True if the given assignee is identified as a bot." +assignee_domain,keyword,true,"Domain associated to the assignee in SortingHat profile." +assignee_gender,keyword,true,"Assignee gender, based on her name (disabled by default)." +assignee_gender_acc,long,true,"Assignee gender accuracy (disabled by default)." +assignee_id,keyword,true,"Assignee ID from SortingHat." +assignee_login,keyword,true,"Assignee's login name from GitLab." +assignee_multi_org_names,keyword,true,"List of the assignee organizations from SortingHat profile." +assignee_name,keyword,true,"Assignee name." +assignee_org_name,keyword,true,"Assignee organization name." +assignee_user_name,keyword,true,"Assignee user name from SortingHat." +assignee_uuid,keyword,true,"Assignee UUID from SortingHat." +author_bot,boolean,true,"True if the given author is identified as a bot, from SortingHat profile." +author_domain,keyword,true,"Domain associated to the author in SortingHat profile." +author_gender,keyword,true,"Author gender, based on her name (disabled by default)." +author_gender_acc,long,true,"Author gender accuracy (disabled by default)." +author_id,keyword,true,"Author ID from SortingHat." +author_login,keyword,true,"Author's login name from GitLab." +author_multi_org_names,keyword,true,"List of the author organizations from SortingHat profile." +author_name,keyword,true,"Author name." +author_org_name,keyword,true,"Author organization name." +author_user_name,keyword,true,"Author user name from SortingHat." +author_uuid,keyword,true,"Author UUID from SortingHat." +body,keyword,true,"Body of the issue/comment." +body_analyzed,text,true,"Body of the issue/comment." +comment_updated_at,date,true,"Date when the comment was updated." +gitlab_repo,keyword,true,"The name of the GitLab repository." +grimoire_creation_date,date,true,"Issue/comment creation date." +id,keyword,true,"Issue/comment ID." +is_gitlab_comment,long,true,"Used to unify issue and issue comments." +is_gitlab_issue,long,true,"Used to separate issues from comments." +is_gitlab_issue_comment,long,true,"Used to separate comments from issues." +issue_closed_at,date,true,"Date when an issue was closed." +issue_created_at,date,true,"Date when an issue was opened." +issue_id,long,true,"Issue ID on GitLab." +issue_id_in_repo,keyword,true,"The issue's ID in the repository." +issue_labels,keyword,true,"The labels assigned to an issue." +issue_state,keyword,true,"State of the issue (open/closed)." +issue_title,keyword,true,"The title of the issue." +issue_title_analyzed,keyword,true,"Issue title split by terms to allow searching." +issue_updated_at,date,true,"Date when the issue was last updated." +issue_url,keyword,true,"Full URL of the issue." +item_type,keyword,true,"The type of the item (issue/comment)." +metadata__enriched_on,date,true,"Date when the data were enriched." +metadata__gelk_backend_name,keyword,true,"Name of the backend used to enrich the data." +metadata__gelk_version,keyword,true,"Version of the backend used to enrich the data." +metadata__timestamp,date,true,"Date when the item was stored in ElasticSearch raw index." +metadata__updated_on,date,true,"Date when the item was updated on its original data source." +origin,keyword,true,"The original URL from which the repository was retrieved from." +project,keyword,true,"Project name." +project_1,keyword,true,"Used if more than one project levels are allowed in the project hierarchy." +reactions.count,long,true,"Number of reactions of a given type." +reactions.type,keyword,true,"Name of reaction to comment/issue." +reactions_total_count,long,true,"Total number of reactions to comment/issue." +repository,keyword,true,"Repository name." +sub_type,keyword,true,"Type of the comment (issue comment)." +tag,keyword,true,"Perceval tag." +time_open_days,float,true,"Time the item is open counted in days." +time_to_close_days,float,true,"Time to close an issue counted in days." +time_to_first_attention,float,true,"Time to first attention to an issue counted in days." +title_analyzed,text,true,"Issue title split by by terms to allow searching." +url,keyword,true,"Url of the issue/comment." +uuid,keyword,true,"Perceval UUID." diff --git a/schema/gitlabcomments_merges.csv b/schema/gitlabcomments_merges.csv new file mode 100644 index 000000000..fc348970d --- /dev/null +++ b/schema/gitlabcomments_merges.csv @@ -0,0 +1,69 @@ +name,type,aggregatable,description +author_bot,boolean,true,"True if the given author is identified as a bot." +author_domain,keyword,true,"Domain associated to the author in SortingHat profile." +author_gender,keyword,true,"Author gender, based on her name (disabled by default)." +author_gender_acc,long,true,"Author gender accuracy (disabled by default)." +author_id,keyword,true,"Author ID from SortingHat." +author_login,keyword,true,"Author's login name from GitLab." +author_multi_org_names,keyword,true,"List of the author organizations from SortingHat profile." +author_name,keyword,true,"Author name." +author_org_name,keyword,true,"Author organization name." +author_user_name,keyword,true,"Author user name from SortingHat." +author_uuid,keyword,true,"Author UUID from SortingHat." +body,keyword,true,"Body of the merge request/comment." +body_analyzed,text,true,"Body of the merge request/comment." +code_merge_duration,float,true,"Difference in days between creation and merging dates." +comment_updated_at,date,true,"Date when the comment was updated." +gitlab_repo,keyword,true,"The name of the GitLab repository." +grimoire_creation_date,date,true,"Merge request/comment creation date." +id,keyword,true,"Merge request/comment ID." +is_gitlab_comment,long,true,"Used to unify merge request and merge request comments." +is_gitlab_merge_comment,long,true,"Used to separate merge requests from comments." +is_gitlab_merge_request,long,true,"Used to separate merge requests from issues." +item_type,keyword,true,"The type of the item (merge request/comment)." +merge_author_login,keyword,true,"Merge author login from GitLab." +merge_author_name,keyword,true,"Merge author name from GitLab." +merge_closed_at,date,true,"Date in which the merge request was closed." +merge_created_at,date,true,"Date in which the merge request was closed." +merge_id,long,true,"Merge request ID in the GitLab repository." +merge_id_in_repo,keyword,true,"The merge request's ID in the repository." +merge_labels,keyword,true,"The labels assigned to an merge request." +merge_merged_at,date,true,"Date when the merge request was merged." +merge_state,keyword,true,"State of the merge request (open/closed/merged)." +merge_status,keyword,true,"State of the merge request (can_be_merged/can_be_closed)." +merge_title,keyword,true,"The title of the merge request." +merge_title_analyzed,keyword,true,"Merge request title split by terms to allow searching." +merge_updated_at,date,true,"Date when the merge request was last updated." +merge_url,keyword,true,"Full URL of the merge request." +merged_by_bot,boolean,true,"True if the merge author is identified as a bot, from SortingHat profile." +merged_by_domain,keyword,true,"Domain associated to the merge author in SortingHat profile." +merged_by_gender,keyword,true,"Merge author gender, based on her name (disabled by default)." +merged_by_gender_acc,long,true,"Merge author gender accuracy (disabled by default)." +merged_by_id,keyword,true,"Merge author ID from SortingHat." +merged_by_multi_org_names,keyword,true,"List of the merge author organizations from SortingHat profile." +merged_by_name,keyword,true,"Merge author name." +merged_by_org_name,keyword,true,"Merge author organization name." +merged_by_user_name,keyword,true,"Merge author user name from SortingHat." +merged_by_uuid,keyword,true,"Merge author UUID from SortingHat." +metadata__enriched_on,date,true,"Date when the data were enriched." +metadata__gelk_backend_name,keyword,true,"Name of the backend used to enrich the data." +metadata__gelk_version,keyword,true,"Version of the backend used to enrich the data." +metadata__timestamp,date,true,"Date when the item was stored in ElasticSearch raw index." +metadata__updated_on,date,true,"Date when the item was updated on its original data source." +num_merge_comments,long,true,"Number of merge request comments." +num_versions,long,true,"Number of versions" +origin,keyword,true,"The original URL from which the repository was retrieved from." +project,keyword,true,"Project name." +project_1,keyword,true,"Used if more than one project levels are allowed in the project hierarchy." +reactions.count,long,true,"Number of reactions of a given type." +reactions.type,keyword,true,"Name of reaction to comment/issue." +reactions_total_count,long,true,"Total number of reactions to comment/issue." +repository,keyword,true,"Repository name." +sub_type,keyword,true,"Type of the comment (merge request comment)." +tag,keyword,true,"Perceval tag." +time_open_days,float,true,"Time the item is open counted in days." +time_to_close_days,float,true,"Time to close a merge request counted in days." +time_to_merge_request_response,float,true,"Time to merge a merge request counted in days." +title_analyzed,text,true,"Issue title split by by terms to allow searching." +url,keyword,true,"Url of the merge request/comment." +uuid,keyword,true,"Perceval UUID." diff --git a/tests/data/gitlabcomments.json b/tests/data/gitlabcomments.json new file mode 100644 index 000000000..0d52b0529 --- /dev/null +++ b/tests/data/gitlabcomments.json @@ -0,0 +1,1735 @@ +[ + { + "backend_name": "GitLab", + "backend_version": "0.12.0", + "category": "issue", + "classified_fields_filtered": null, + "data": { + "_links": { + "award_emoji": "https://gitlab.com/api/v4/projects/16307213/issues/25/award_emoji", + "notes": "https://gitlab.com/api/v4/projects/16307213/issues/25/notes", + "project": "https://gitlab.com/api/v4/projects/16307213", + "self": "https://gitlab.com/api/v4/projects/16307213/issues/25" + }, + "assignee": null, + "assignees": [], + "author": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1503111/avatar.png", + "id": 1503111, + "name": "Vibhoothi", + "state": "active", + "username": "mindfreeze", + "web_url": "https://gitlab.com/mindfreeze" + }, + "award_emoji_data": [ + { + "awardable_id": 32985061, + "awardable_type": "Issue", + "created_at": "2020-05-26T16:51:48.872Z", + "id": 4567403, + "name": "thumbsdown", + "updated_at": "2020-05-26T16:51:48.872Z", + "user": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/2409720/avatar.png", + "id": 2409720, + "name": "Venu Vardhan Reddy Tekula", + "state": "active", + "username": "vchrombie", + "web_url": "https://gitlab.com/vchrombie" + } + } + ], + "closed_at": null, + "closed_by": null, + "confidential": false, + "created_at": "2020-04-07T11:31:36.167Z", + "description": "The current Profile Page is very vague and has little details \n\nThings would be good if we have \n- [X] More Details \n- [X] Links \n- [X] Option to edit details\n- [ ] Option to Change/Remove the current DP\n- [X] Removal of GitHub Username at top and start using [amFOSS Page](https://amfoss.in/@abhishekbvs)", + "discussion_locked": null, + "downvotes": 1, + "due_date": null, + "epic": null, + "epic_iid": null, + "has_tasks": true, + "id": 32985061, + "iid": 25, + "labels": [ + "UI", + "enhancement", + "feature" + ], + "merge_requests_count": 0, + "milestone": null, + "moved_to_id": null, + "notes_data": [ + { + "attachment": null, + "author": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1503111/avatar.png", + "id": 1503111, + "name": "Vibhoothi", + "state": "active", + "username": "mindfreeze", + "web_url": "https://gitlab.com/mindfreeze" + }, + "award_emoji_data": [], + "body": "changed the description", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-07T11:32:24.925Z", + "id": 319122638, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-07T11:32:24.926Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "marked the task **Option to edit details** as completed", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-18T17:56:58.184Z", + "id": 326691203, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-18T17:56:58.188Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "marked the task **More Details** as completed", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:05:23.025Z", + "id": 328034243, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:05:23.028Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "marked the task **Links** as completed", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:05:23.062Z", + "id": 328034244, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:05:23.064Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "marked the task **Removal of GitHub Username at top and start using [amFOSS Page](https://amfoss.in/@abhishekbvs)** as completed", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:05:23.094Z", + "id": 328034245, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:05:23.096Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + }, + "award_emoji_data": [ + { + "awardable_id": 341879486, + "awardable_type": "Note", + "created_at": "2020-05-20T12:21:42.312Z", + "id": 4508195, + "name": "basketball", + "updated_at": "2020-05-20T12:21:42.312Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + } + } + ], + "body": "I also have few more to add\n\n- [ ] If the gitlab/github username doesn't exist we can hide the icon\n\n\n\n- [ ] The list can be matched to parent and the padding can be set to the members inside the list\n\n", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-05-13T15:11:57.647Z", + "id": 341879486, + "noteable_id": 32985061, + "noteable_iid": 25, + "noteable_type": "Issue", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-05-13T15:11:57.647Z" + } + ], + "project_id": 16307213, + "references": { + "full": "amfoss/cms-mobile#25", + "relative": "#25", + "short": "#25" + }, + "state": "opened", + "task_completion_status": { + "completed_count": 4, + "count": 5 + }, + "task_status": "4 of 5 tasks completed", + "time_stats": { + "human_time_estimate": null, + "human_total_time_spent": null, + "time_estimate": 0, + "total_time_spent": 0 + }, + "title": "Improve the Profile Page", + "updated_at": "2020-05-13T15:11:57.668Z", + "upvotes": 1, + "user_notes_count": 1, + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/issues/25", + "weight": null + }, + "origin": "https://gitlab.com/amfoss/cms-mobile", + "perceval_version": "0.14.0", + "search_fields": { + "groups": null, + "iid": 25, + "item_id": "32985061", + "owner": "amfoss", + "project": "cms-mobile" + }, + "tag": "https://gitlab.com/amfoss/cms-mobile", + "timestamp": 1591699837.969952, + "updated_on": 1589382717.668, + "uuid": "fa0223023445bd85fdb201e960d816fe3a99b306" + }, + { + "backend_name": "GitLab", + "backend_version": "0.12.0", + "category": "merge_request", + "classified_fields_filtered": null, + "data": { + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "approvals_before_merge": null, + "assignee": null, + "assignees": [], + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "blocking_discussions_resolved": true, + "changes_count": "1", + "closed_at": "2020-04-24T12:41:55.207Z", + "closed_by": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "created_at": "2020-04-20T19:17:54.576Z", + "description": "Closes #18\r\nCloses #28 \r\n\r\nChanges: Add alert notification if a user doesn't enter his/her credentials.\r\n\r\nScreenshots of the change: \r\n\r\n![login_validation](/uploads/173f17e178d90484db1693c30019fd7b/login_validation.gif)", + "diff_refs": { + "base_sha": "6747f667897a462b37ddc6ab1a6920705774d9d2", + "head_sha": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "start_sha": "6747f667897a462b37ddc6ab1a6920705774d9d2" + }, + "discussion_locked": null, + "downvotes": 0, + "first_contribution": false, + "first_deployed_to_production_at": null, + "force_remove_source_branch": true, + "has_conflicts": false, + "head_pipeline": { + "before_sha": "a2d3376f1cb6c7df2e49dffdc24999f688daac5d", + "committed_at": null, + "coverage": null, + "created_at": "2020-04-24T11:38:17.909Z", + "detailed_status": { + "details_path": "/athiranair2000/cms-mobile/-/pipelines/139527982", + "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", + "group": "success", + "has_details": true, + "icon": "status_success", + "illustration": null, + "label": "passed", + "text": "passed", + "tooltip": "passed" + }, + "duration": 523, + "finished_at": "2020-04-24T11:47:02.978Z", + "id": 139527982, + "ref": "cms-18", + "sha": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "started_at": "2020-04-24T11:38:19.464Z", + "status": "success", + "tag": false, + "updated_at": "2020-04-24T11:47:02.984Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "web_url": "https://gitlab.com/athiranair2000/cms-mobile/-/pipelines/139527982", + "yaml_errors": null + }, + "id": 56306189, + "iid": 41, + "labels": [], + "latest_build_finished_at": "2020-04-24T11:47:02.978Z", + "latest_build_started_at": "2020-04-24T11:38:19.464Z", + "merge_commit_sha": null, + "merge_error": null, + "merge_status": "can_be_merged", + "merge_when_pipeline_succeeds": false, + "merged_at": null, + "merged_by": null, + "milestone": null, + "notes_data": [ + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "@athiranair2000 thanks a lot for sending this MR :)\n\nAs you notice, the text seems to jump out of the decoration box when the error is shown. \nIt seems to be an issue with flutter itself, though I'm not very sure. \nA workaround I thought of is to increase the size of the input box so that even if the text jumps up, it will stay inside the box and therefore not look half inside and half outside the box.\n\n@padth4i @immadisairaj If you know of any better fix for this please mention.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-20T19:59:05.344Z", + "id": 327583642, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-04-20T20:01:59.377Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "changed the description", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-20T20:03:30.053Z", + "id": 327587422, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-20T20:03:30.056Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "@Chromicle I don't think this will work. A validator also returns and sets the error text without being explicitly told to do so. \nSo in both cases error text is being set itself.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T04:50:41.178Z", + "id": 327749521, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T04:50:41.178Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/924a72e21eb76c29ed58ffb96f900781?s=80&d=identicon", + "id": 4248740, + "name": "Ajay Prabhakar", + "state": "active", + "username": "Chromicle", + "web_url": "https://gitlab.com/Chromicle" + }, + "award_emoji_data": [], + "body": "Great work @athiranair2000, instead of using `validator` you can use errortext in decoration\n\nPlease go through this https://stackoverflow.com/questions/53424916/textfield-validation-in-flutter", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T04:23:16.010Z", + "id": 327743031, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T04:50:41.275Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "Yeah, this is the same as the thing I suggested. It won't look good in our approach unless we increase the height to make the text stay inside the text box even after jumping.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T04:52:11.801Z", + "id": 327750110, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T04:52:11.801Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + }, + "award_emoji_data": [], + "body": "Also, you can go through [this](https://github.com/flutter/flutter/issues/15400) link. Maybe this is related to that.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T04:33:26.933Z", + "id": 327745403, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T04:52:11.874Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + }, + "award_emoji_data": [], + "body": "Yeah, I guess we can have it like the way you suggested or maybe we can have a separate Text right above the submit button. I am fine with any of the solution. Check which looks better and implement that.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T05:37:32.752Z", + "id": 327761712, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T05:37:32.752Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "@Chromicle @immadisairaj @athiranair2000 IMO even after increasing the size, the UI will look a bit odd. \nI say since we're using a box to wrap text around, instead of using the errorText approach directly/through a validator, we can show a toast instead.\n\nThat will keep our UI neat and show a user a notification as well.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T04:53:40.321Z", + "id": 327750407, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T05:37:32.834Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "I thought of a new text view. But I think our current UI looks good. If we change some spacings, it might look awkward. So I think toast is the best option. \n\n@athiranair2000 can you please make a toast in the validator instead of returning a string for the error text?", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T05:48:49.223Z", + "id": 327764854, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": true, + "resolved": false, + "resolved_by": null, + "system": false, + "type": "DiscussionNote", + "updated_at": "2020-04-21T05:49:02.911Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "body": "added 4 commits\n\n\n\n[Compare with previous version](/amfoss/cms-mobile/-/merge_requests/41/diffs?diff_id=87275919&start_sha=a2d3376f1cb6c7df2e49dffdc24999f688daac5d)", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-24T11:38:22.609Z", + "id": 330882340, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-24T11:38:22.611Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "body": "closed", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-24T12:41:55.245Z", + "id": 330928663, + "noteable_id": 56306189, + "noteable_iid": 41, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-24T12:41:55.247Z" + } + ], + "pipeline": { + "created_at": "2020-04-24T11:38:17.909Z", + "id": 139527982, + "ref": "cms-18", + "sha": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "status": "success", + "updated_at": "2020-04-24T11:47:02.984Z", + "web_url": "https://gitlab.com/athiranair2000/cms-mobile/-/pipelines/139527982" + }, + "project_id": 16307213, + "reference": "!41", + "references": { + "full": "amfoss/cms-mobile!41", + "relative": "!41", + "short": "!41" + }, + "sha": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "should_remove_source_branch": null, + "source_branch": "cms-18", + "source_project_id": 18249015, + "squash": false, + "squash_commit_sha": null, + "state": "closed", + "subscribed": false, + "target_branch": "master", + "target_project_id": 16307213, + "task_completion_status": { + "completed_count": 0, + "count": 0 + }, + "time_stats": { + "human_time_estimate": null, + "human_total_time_spent": null, + "time_estimate": 0, + "total_time_spent": 0 + }, + "title": "fix: Notification in login page", + "updated_at": "2020-04-24T12:41:55.107Z", + "upvotes": 0, + "user": { + "can_merge": false + }, + "user_notes_count": 8, + "versions_data": [ + { + "base_commit_sha": "6747f667897a462b37ddc6ab1a6920705774d9d2", + "commits": [ + { + "author_email": "athinair2000@gmail.com", + "author_name": "athiranair2000", + "authored_date": "2020-04-20T19:01:09.000Z", + "committed_date": "2020-04-24T11:38:16.000Z", + "committer_email": "athinair2000@gmail.com", + "committer_name": "Athira", + "created_at": "2020-04-24T11:38:16.000Z", + "id": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "message": "fix: Notification in login page\n", + "parent_ids": [], + "short_id": "67b9ec6d", + "title": "fix: Notification in login page", + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/commit/67b9ec6db4362e8f2265c66f5f310d317ce1df8c" + } + ], + "created_at": "2020-04-24T11:38:18.595Z", + "head_commit_sha": "67b9ec6db4362e8f2265c66f5f310d317ce1df8c", + "id": 87275919, + "merge_request_id": 56306189, + "real_size": "1", + "start_commit_sha": "6747f667897a462b37ddc6ab1a6920705774d9d2", + "state": "collected" + }, + { + "base_commit_sha": "8cda588e5fa519c92d9832631147010a075fa76b", + "commits": [ + { + "author_email": "athinair2000@gmail.com", + "author_name": "athiranair2000", + "authored_date": "2020-04-20T19:01:09.000Z", + "committed_date": "2020-04-20T19:01:09.000Z", + "committer_email": "athinair2000@gmail.com", + "committer_name": "athiranair2000", + "created_at": "2020-04-20T19:01:09.000Z", + "id": "a2d3376f1cb6c7df2e49dffdc24999f688daac5d", + "message": "fix: Notification in login page\n", + "parent_ids": [], + "short_id": "a2d3376f", + "title": "fix: Notification in login page", + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/commit/a2d3376f1cb6c7df2e49dffdc24999f688daac5d" + } + ], + "created_at": "2020-04-20T19:17:54.959Z", + "head_commit_sha": "a2d3376f1cb6c7df2e49dffdc24999f688daac5d", + "id": 86399689, + "merge_request_id": 56306189, + "real_size": "1", + "start_commit_sha": "8cda588e5fa519c92d9832631147010a075fa76b", + "state": "collected" + } + ], + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/merge_requests/41", + "work_in_progress": false + }, + "origin": "https://gitlab.com/amfoss/cms-mobile", + "perceval_version": "0.14.0", + "search_fields": { + "groups": null, + "iid": 41, + "item_id": "56306189", + "owner": "amfoss", + "project": "cms-mobile" + }, + "tag": "https://gitlab.com/amfoss/cms-mobile", + "timestamp": 1591699045.756355, + "updated_on": 1587732115.107, + "uuid": "5735986bb9f2bb58920bb5a263e6e85f1308beb1" + }, + { + "backend_name": "GitLab", + "backend_version": "0.12.0", + "category": "merge_request", + "classified_fields_filtered": null, + "data": { + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "approvals_before_merge": null, + "assignee": null, + "assignees": [], + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/924a72e21eb76c29ed58ffb96f900781?s=80&d=identicon", + "id": 4248740, + "name": "Ajay Prabhakar", + "state": "active", + "username": "Chromicle", + "web_url": "https://gitlab.com/Chromicle" + }, + "award_emoji_data": [ + { + "awardable_id": 56390497, + "awardable_type": "MergeRequest", + "created_at": "2020-05-20T08:10:03.606Z", + "id": 4503657, + "name": "rocket", + "updated_at": "2020-05-20T08:10:03.606Z", + "user": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/2409720/avatar.png", + "id": 2409720, + "name": "Venu Vardhan Reddy Tekula", + "state": "active", + "username": "vchrombie", + "web_url": "https://gitlab.com/vchrombie" + } + } + ], + "blocking_discussions_resolved": true, + "changes_count": "6", + "closed_at": null, + "closed_by": null, + "created_at": "2020-04-21T10:59:26.163Z", + "description": "#### Changes\nAdded more details in profile screen\n\n* About \n* Github link\n* GitLab link\n* Telegram link\n* Last seen in lab\n\nUpdated some icons \n\n#### ScreenShot\n", + "diff_refs": { + "base_sha": "8cda588e5fa519c92d9832631147010a075fa76b", + "head_sha": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "start_sha": "8cda588e5fa519c92d9832631147010a075fa76b" + }, + "discussion_locked": null, + "downvotes": 0, + "first_contribution": false, + "first_deployed_to_production_at": null, + "force_remove_source_branch": true, + "has_conflicts": false, + "head_pipeline": { + "before_sha": "0000000000000000000000000000000000000000", + "committed_at": null, + "coverage": null, + "created_at": "2020-04-21T10:52:37.577Z", + "detailed_status": { + "details_path": "/Chromicle/cms-mobile/-/pipelines/138240206", + "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", + "group": "success", + "has_details": true, + "icon": "status_success", + "illustration": null, + "label": "passed", + "text": "passed", + "tooltip": "passed" + }, + "duration": 474, + "finished_at": "2020-04-21T11:00:32.395Z", + "id": 138240206, + "ref": "prof", + "sha": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "started_at": "2020-04-21T10:52:38.076Z", + "status": "success", + "tag": false, + "updated_at": "2020-04-21T11:00:32.401Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/924a72e21eb76c29ed58ffb96f900781?s=80&d=identicon", + "id": 4248740, + "name": "Ajay Prabhakar", + "state": "active", + "username": "Chromicle", + "web_url": "https://gitlab.com/Chromicle" + }, + "web_url": "https://gitlab.com/Chromicle/cms-mobile/-/pipelines/138240206", + "yaml_errors": null + }, + "id": 56390497, + "iid": 42, + "labels": [ + "enhancement", + "feature" + ], + "latest_build_finished_at": "2020-04-21T11:00:32.395Z", + "latest_build_started_at": "2020-04-21T10:52:38.076Z", + "merge_commit_sha": null, + "merge_error": null, + "merge_status": "can_be_merged", + "merge_when_pipeline_succeeds": false, + "merged_at": "2020-04-21T11:04:45.087Z", + "merged_by": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "milestone": null, + "notes_data": [ + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "approved this merge request", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:02:47.868Z", + "id": 328032741, + "noteable_id": 56390497, + "noteable_iid": 42, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:02:47.870Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + }, + "award_emoji_data": [], + "body": "approved this merge request", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:04:04.989Z", + "id": 328033475, + "noteable_id": 56390497, + "noteable_iid": 42, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:04:04.990Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "merged", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:04:45.260Z", + "id": 328033866, + "noteable_id": 56390497, + "noteable_iid": 42, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-21T11:04:45.262Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + }, + "award_emoji_data": [], + "body": "@Chromicle Maybe as we are adding Flutter Icons, see if any icons in other screens also needed changes.", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:04:57.343Z", + "id": 328033971, + "noteable_id": 56390497, + "noteable_iid": 42, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-04-21T11:04:57.343Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/924a72e21eb76c29ed58ffb96f900781?s=80&d=identicon", + "id": 4248740, + "name": "Ajay Prabhakar", + "state": "active", + "username": "Chromicle", + "web_url": "https://gitlab.com/Chromicle" + }, + "award_emoji_data": [ + { + "awardable_id": 328038233, + "awardable_type": "Note", + "created_at": "2020-04-21T11:13:52.961Z", + "id": 4181914, + "name": "thumbsup_tone1", + "updated_at": "2020-04-21T11:13:52.961Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/96b58cb8a1bd5aefbaa8f4b5b96d9863?s=80&d=identicon", + "id": 5028715, + "name": "Sai Rajendra Immadi", + "state": "active", + "username": "immadisairaj", + "web_url": "https://gitlab.com/immadisairaj" + } + } + ], + "body": "yeah, I already made some changes in drawer and bottom navigation\\\n", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-21T11:12:22.820Z", + "id": 328038233, + "noteable_id": 56390497, + "noteable_iid": 42, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-04-21T11:12:22.820Z" + } + ], + "pipeline": { + "created_at": "2020-04-21T10:52:37.577Z", + "id": 138240206, + "ref": "prof", + "sha": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "status": "success", + "updated_at": "2020-04-21T11:00:32.401Z", + "web_url": "https://gitlab.com/Chromicle/cms-mobile/-/pipelines/138240206" + }, + "project_id": 16307213, + "reference": "!42", + "references": { + "full": "amfoss/cms-mobile!42", + "relative": "!42", + "short": "!42" + }, + "sha": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "should_remove_source_branch": null, + "source_branch": "prof", + "source_project_id": 17875277, + "squash": false, + "squash_commit_sha": null, + "state": "merged", + "subscribed": true, + "target_branch": "master", + "target_project_id": 16307213, + "task_completion_status": { + "completed_count": 0, + "count": 0 + }, + "time_stats": { + "human_time_estimate": null, + "human_total_time_spent": null, + "time_estimate": 0, + "total_time_spent": 0 + }, + "title": "feat: Add more details in profile screen", + "updated_at": "2020-04-21T11:12:22.847Z", + "upvotes": 0, + "user": { + "can_merge": false + }, + "user_notes_count": 2, + "versions_data": [ + { + "base_commit_sha": "8cda588e5fa519c92d9832631147010a075fa76b", + "commits": [ + { + "author_email": "ajayprabhakar369@gmail.com", + "author_name": "chromicle", + "authored_date": "2020-04-21T10:52:12.000Z", + "committed_date": "2020-04-21T10:52:12.000Z", + "committer_email": "ajayprabhakar369@gmail.com", + "committer_name": "chromicle", + "created_at": "2020-04-21T10:52:12.000Z", + "id": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "message": "feat: Add more details in profile screen\n", + "parent_ids": [], + "short_id": "5b61c15a", + "title": "feat: Add more details in profile screen", + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/commit/5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3" + } + ], + "created_at": "2020-04-21T10:59:26.398Z", + "head_commit_sha": "5b61c15a2d53b6a34fd880f4f2a3d236ab1ea7b3", + "id": 86533739, + "merge_request_id": 56390497, + "real_size": "6", + "start_commit_sha": "8cda588e5fa519c92d9832631147010a075fa76b", + "state": "collected" + } + ], + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/merge_requests/42", + "work_in_progress": false + }, + "origin": "https://gitlab.com/amfoss/cms-mobile", + "perceval_version": "0.14.0", + "search_fields": { + "groups": null, + "iid": 42, + "item_id": "56390497", + "owner": "amfoss", + "project": "cms-mobile" + }, + "tag": "https://gitlab.com/amfoss/cms-mobile", + "timestamp": 1591698996.703761, + "updated_on": 1587467542.847, + "uuid": "3c6480ce858722bad95955a3fc65daa8c0f8df0c" + }, + { + "backend_name": "GitLab", + "backend_version": "0.12.0", + "category": "issue", + "classified_fields_filtered": null, + "data": { + "_links": { + "award_emoji": "https://gitlab.com/api/v4/projects/16307213/issues/18/award_emoji", + "notes": "https://gitlab.com/api/v4/projects/16307213/issues/18/notes", + "project": "https://gitlab.com/api/v4/projects/16307213", + "self": "https://gitlab.com/api/v4/projects/16307213/issues/18" + }, + "assignee": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "assignees": [ + { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + } + ], + "author": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/5298601/avatar.png", + "id": 5298601, + "name": "Shashank Priyadarshi", + "state": "active", + "username": "robustTechie", + "web_url": "https://gitlab.com/robustTechie" + }, + "award_emoji_data": [], + "closed_at": "2020-04-26T05:52:44.262Z", + "closed_by": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "confidential": false, + "created_at": "2020-04-06T17:05:34.517Z", + "description": "**Describe the bug**\n\nIf the user presses the Login button without entering anything in username and password then it doesn't show any alert notification.\n\n**Screenshot**\n\n![No_message](/uploads/82d66a5723b4c850f0c52e3019f89ea5/No_message.gif)", + "discussion_locked": null, + "downvotes": 0, + "due_date": null, + "epic": null, + "epic_iid": null, + "has_tasks": false, + "id": 32953131, + "iid": 18, + "labels": [ + "enhancement", + "good first issue" + ], + "merge_requests_count": 2, + "milestone": null, + "moved_to_id": null, + "notes_data": [ + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "body": "Hi, can I work on this issue?", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-19T18:41:26.580Z", + "id": 326914119, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-04-19T18:41:26.580Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "@athiranair2000 sure, go ahead :)", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-19T18:43:49.324Z", + "id": 326914411, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": false, + "type": null, + "updated_at": "2020-04-19T18:43:49.324Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "assigned to @athiranair2000", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-19T18:44:00.690Z", + "id": 326914447, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-19T18:44:00.692Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "body": "mentioned in merge request !41", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-20T19:17:56.040Z", + "id": 327563457, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-20T19:17:56.042Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/2bf24a4231306842f743a28529e3347e?s=80&d=identicon", + "id": 5162683, + "name": "Athira", + "state": "active", + "username": "athiranair2000", + "web_url": "https://gitlab.com/athiranair2000" + }, + "award_emoji_data": [], + "body": "mentioned in merge request !46", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-24T13:30:12.675Z", + "id": 330962993, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-24T13:30:12.677Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "closed via merge request !46", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-26T05:52:44.430Z", + "id": 331502229, + "noteable_id": 32953131, + "noteable_iid": 18, + "noteable_type": "Issue", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-26T05:52:44.431Z" + } + ], + "project_id": 16307213, + "references": { + "full": "amfoss/cms-mobile#18", + "relative": "#18", + "short": "#18" + }, + "state": "closed", + "task_completion_status": { + "completed_count": 0, + "count": 0 + }, + "time_stats": { + "human_time_estimate": null, + "human_total_time_spent": null, + "time_estimate": 0, + "total_time_spent": 0 + }, + "title": "No notification in login page", + "updated_at": "2020-04-26T05:52:44.358Z", + "upvotes": 0, + "user_notes_count": 2, + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/issues/18", + "weight": null + }, + "origin": "https://gitlab.com/amfoss/cms-mobile", + "perceval_version": "0.14.0", + "search_fields": { + "groups": null, + "iid": 18, + "item_id": "32953131", + "owner": "amfoss", + "project": "cms-mobile" + }, + "tag": "https://gitlab.com/amfoss/cms-mobile", + "timestamp": 1591699796.859146, + "updated_on": 1587880364.358, + "uuid": "b0266ccecda35146780c076ad009015cf24927b3" + }, + { + "backend_name": "GitLab", + "backend_version": "0.12.0", + "category": "merge_request", + "classified_fields_filtered": null, + "data": { + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "approvals_before_merge": null, + "assignee": null, + "assignees": [], + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/a5f39a251241ebd0ce9aab4ae28ba04c?s=80&d=identicon", + "id": 4778175, + "name": "Vaishnav", + "state": "active", + "username": "padth4i", + "web_url": "https://gitlab.com/padth4i" + }, + "award_emoji_data": [ + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-05-20T07:56:31.195Z", + "id": 4503450, + "name": "thumbsup", + "updated_at": "2020-05-20T07:56:31.195Z", + "user": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/2409720/avatar.png", + "id": 2409720, + "name": "Venu Vardhan Reddy Tekula", + "state": "active", + "username": "vchrombie", + "web_url": "https://gitlab.com/vchrombie" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:14:55.656Z", + "id": 4688007, + "name": "thumbsup", + "updated_at": "2020-06-06T19:14:55.656Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/b2503e814e0b2c4fb93aed9fe71bd27d?s=80&d=identicon", + "id": 5276179, + "name": "Harshith Pabbati", + "state": "active", + "username": "harshithpabbati", + "web_url": "https://gitlab.com/harshithpabbati" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:15:53.560Z", + "id": 4688008, + "name": "basketball", + "updated_at": "2020-06-06T19:15:53.560Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/b2503e814e0b2c4fb93aed9fe71bd27d?s=80&d=identicon", + "id": 5276179, + "name": "Harshith Pabbati", + "state": "active", + "username": "harshithpabbati", + "web_url": "https://gitlab.com/harshithpabbati" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:16:55.091Z", + "id": 4688010, + "name": "thumbsdown", + "updated_at": "2020-06-06T19:16:55.091Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/b2503e814e0b2c4fb93aed9fe71bd27d?s=80&d=identicon", + "id": 5276179, + "name": "Harshith Pabbati", + "state": "active", + "username": "harshithpabbati", + "web_url": "https://gitlab.com/harshithpabbati" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:20:11.764Z", + "id": 4688016, + "name": "thumbsup", + "updated_at": "2020-06-06T19:20:11.764Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:20:58.920Z", + "id": 4688018, + "name": "basketball", + "updated_at": "2020-06-06T19:20:58.920Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:22:23.034Z", + "id": 4688021, + "name": "rocket", + "updated_at": "2020-06-06T19:22:23.034Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/dcb6dca61de3778b46b14fba4931184a?s=80&d=identicon", + "id": 2378565, + "name": "Abhishek Bvs", + "state": "active", + "username": "abhishekbvs", + "web_url": "https://gitlab.com/abhishekbvs" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:22:25.159Z", + "id": 4688022, + "name": "thumbsup", + "updated_at": "2020-06-06T19:22:25.159Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/dcb6dca61de3778b46b14fba4931184a?s=80&d=identicon", + "id": 2378565, + "name": "Abhishek Bvs", + "state": "active", + "username": "abhishekbvs", + "web_url": "https://gitlab.com/abhishekbvs" + } + }, + { + "awardable_id": 56958021, + "awardable_type": "MergeRequest", + "created_at": "2020-06-06T19:22:45.464Z", + "id": 4688025, + "name": "8ball", + "updated_at": "2020-06-06T19:22:45.464Z", + "user": { + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/2409720/avatar.png", + "id": 2409720, + "name": "Venu Vardhan Reddy Tekula", + "state": "active", + "username": "vchrombie", + "web_url": "https://gitlab.com/vchrombie" + } + } + ], + "blocking_discussions_resolved": true, + "changes_count": "6", + "closed_at": null, + "closed_by": null, + "created_at": "2020-04-26T16:31:32.694Z", + "description": "Changes: \nWhen the user logs in to the app for the first time, their credentials get stored in the app database. The next time the app is opened, the database is checked for credentials. If they exist, the app navigates directly to the home page.\n\nVideo:\n![Screenrecorder-2020-04-26-21-55-39-488](/uploads/a1d9a80934fa38a9925240222f44fde5/Screenrecorder-2020-04-26-21-55-39-488.mp4)", + "diff_refs": { + "base_sha": "bbba6a149b0304495fe6cfd5c087188bdb88cebd", + "head_sha": "4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819", + "start_sha": "bbba6a149b0304495fe6cfd5c087188bdb88cebd" + }, + "discussion_locked": null, + "downvotes": 1, + "first_contribution": false, + "first_deployed_to_production_at": null, + "force_remove_source_branch": true, + "has_conflicts": false, + "head_pipeline": { + "before_sha": "bbba6a149b0304495fe6cfd5c087188bdb88cebd", + "committed_at": null, + "coverage": null, + "created_at": "2020-04-26T15:23:13.859Z", + "detailed_status": { + "details_path": "/padth4i/cms-mobile/-/pipelines/139987704", + "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", + "group": "success", + "has_details": true, + "icon": "status_success", + "illustration": null, + "label": "passed", + "text": "passed", + "tooltip": "passed" + }, + "duration": 469, + "finished_at": "2020-04-26T15:31:04.330Z", + "id": 139987704, + "ref": "rememberMe", + "sha": "4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819", + "started_at": "2020-04-26T15:23:15.096Z", + "status": "success", + "tag": false, + "updated_at": "2020-04-26T15:31:04.337Z", + "user": { + "avatar_url": "https://secure.gravatar.com/avatar/a5f39a251241ebd0ce9aab4ae28ba04c?s=80&d=identicon", + "id": 4778175, + "name": "Vaishnav", + "state": "active", + "username": "padth4i", + "web_url": "https://gitlab.com/padth4i" + }, + "web_url": "https://gitlab.com/padth4i/cms-mobile/-/pipelines/139987704", + "yaml_errors": null + }, + "id": 56958021, + "iid": 50, + "labels": [], + "latest_build_finished_at": null, + "latest_build_started_at": null, + "merge_commit_sha": null, + "merge_error": null, + "merge_status": "can_be_merged", + "merge_when_pipeline_succeeds": false, + "merged_at": "2020-04-26T16:35:24.147Z", + "merged_by": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "username": "yashk2000", + "state": "active", + "web_url": "https://gitlab.com/yashk2000" + }, + "milestone": null, + "notes_data": [ + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "approved this merge request", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-26T16:35:21.265Z", + "id": 331598319, + "noteable_id": 56958021, + "noteable_iid": 50, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-26T16:35:21.267Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "merged", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-26T16:35:24.319Z", + "id": 331598323, + "noteable_id": 56958021, + "noteable_iid": 50, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-26T16:35:24.322Z" + }, + { + "attachment": null, + "author": { + "avatar_url": "https://secure.gravatar.com/avatar/7db83a0a0c0d8ec464bf562143e669db?s=80&d=identicon", + "id": 3418756, + "name": "Yash Khare", + "state": "active", + "username": "yashk2000", + "web_url": "https://gitlab.com/yashk2000" + }, + "award_emoji_data": [], + "body": "mentioned in issue #29", + "commands_changes": {}, + "confidential": false, + "created_at": "2020-04-26T16:36:54.759Z", + "id": 331598567, + "noteable_id": 56958021, + "noteable_iid": 50, + "noteable_type": "MergeRequest", + "resolvable": false, + "system": true, + "type": null, + "updated_at": "2020-04-26T16:36:54.760Z" + } + ], + "pipeline": null, + "project_id": 16307213, + "reference": "!50", + "references": { + "full": "amfoss/cms-mobile!50", + "relative": "!50", + "short": "!50" + }, + "sha": "4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819", + "should_remove_source_branch": null, + "source_branch": "rememberMe", + "source_project_id": 17885307, + "squash": false, + "squash_commit_sha": null, + "state": "merged", + "subscribed": true, + "target_branch": "master", + "target_project_id": 16307213, + "task_completion_status": { + "completed_count": 0, + "count": 0 + }, + "time_stats": { + "human_time_estimate": null, + "human_total_time_spent": null, + "time_estimate": 0, + "total_time_spent": 0 + }, + "title": "feat: Remember user details after login", + "updated_at": "2020-04-26T16:36:54.783Z", + "upvotes": 4, + "user": { + "can_merge": false + }, + "user_notes_count": 0, + "versions_data": [ + { + "base_commit_sha": "bbba6a149b0304495fe6cfd5c087188bdb88cebd", + "commits": [ + { + "author_email": "vaishnavs4201@gmail.com", + "author_name": "Vaishnav Sivaprasad", + "authored_date": "2020-04-26T15:24:07.000Z", + "committed_date": "2020-04-26T15:24:07.000Z", + "committer_email": "vaishnavs4201@gmail.com", + "committer_name": "Vaishnav Sivaprasad", + "created_at": "2020-04-26T15:24:07.000Z", + "id": "4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819", + "message": "feat: Remember user details after login\n", + "parent_ids": [], + "short_id": "4d9ffd65", + "title": "feat: Remember user details after login", + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/commit/4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819" + } + ], + "created_at": "2020-04-26T16:31:32.840Z", + "head_commit_sha": "4d9ffd65c453d83c8a6ec5fe26e3fe61f133d819", + "id": 87484095, + "merge_request_id": 56958021, + "real_size": "6", + "start_commit_sha": "bbba6a149b0304495fe6cfd5c087188bdb88cebd", + "state": "collected" + } + ], + "web_url": "https://gitlab.com/amfoss/cms-mobile/-/merge_requests/50", + "work_in_progress": false + }, + "origin": "https://gitlab.com/amfoss/cms-mobile", + "perceval_version": "0.14.0", + "search_fields": { + "groups": null, + "iid": 50, + "item_id": "56958021", + "owner": "amfoss", + "project": "cms-mobile" + }, + "tag": "https://gitlab.com/amfoss/cms-mobile", + "timestamp": 1591699121.686227, + "updated_on": 1587919014.783, + "uuid": "55e9da339312d5e5fa13d687cd36312c79da7dc4" + } +] \ No newline at end of file diff --git a/tests/test_gitlabcomments.py b/tests/test_gitlabcomments.py new file mode 100644 index 000000000..c78f153fb --- /dev/null +++ b/tests/test_gitlabcomments.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015-2020 Bitergia +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authors: +# Venu Vardhan Reddy Tekula +# + + +import logging +import unittest +import unittest.mock + +from base import TestBaseBackend +from grimoire_elk.enriched.utils import REPO_LABELS +from grimoire_elk.raw.gitlab import GitLabOcean + + +class TestGitLabComments(TestBaseBackend): + """Test GitLabComments backend""" + + connector = "gitlabcomments" + ocean_index = "test_" + connector + enrich_index = "test_" + connector + "_enrich" + + def test_has_identities(self): + """Test value of has_identities method""" + + enrich_backend = self.connectors[self.connector][2]() + self.assertTrue(enrich_backend.has_identities()) + + def test_items_to_raw(self): + """Test whether JSON items are properly inserted into ES""" + + result = self._test_items_to_raw() + + self.assertGreater(result['items'], 0) + self.assertGreater(result['raw'], 0) + self.assertEqual(result['items'], result['raw']) + + def test_raw_to_enrich(self): + """Test whether the raw index is properly enriched""" + + result = self._test_raw_to_enrich() + + self.assertGreater(result['raw'], 0) + self.assertGreater(result['enrich'], 0) + + enrich_backend = self.connectors[self.connector][2]() + + item = self.items[0] + eitem = enrich_backend.get_rich_item(item) + self.assertEqual(item['category'], 'issue') + self.assertNotEqual(eitem['issue_state'], 'closed') + self.assertEqual(eitem['issue_labels'], ['UI', 'enhancement', 'feature']) + self.assertEqual(eitem['reactions_total_count'], 1) + self.assertEqual(eitem['reactions'][0]['type'], 'thumbsdown') + self.assertEqual(eitem['reactions'][0]['count'], 1) + self.assertEqual(eitem['issue_id_in_repo'], '25') + self.assertEqual(eitem['issue_created_at'], '2020-04-07T11:31:36.167Z') + + item = self.items[1] + eitem = enrich_backend.get_rich_item(item) + self.assertEqual(item['category'], 'merge_request') + self.assertEqual(eitem['merge_state'], 'closed') + self.assertEqual(eitem['merge_labels'], []) + self.assertEqual(eitem['reactions_total_count'], 0) + self.assertEqual(eitem['reactions'], []) + self.assertNotIn(eitem['time_to_merge_request_response'], eitem) + self.assertEqual(eitem['time_to_close_days'], eitem['time_open_days']) + self.assertEqual(eitem['merge_id_in_repo'], '41') + self.assertEqual(eitem['merge_created_at'], '2020-04-20T19:17:54.576Z') + + item = self.items[2] + eitem = enrich_backend.get_rich_item(item) + self.assertEqual(item['category'], 'merge_request') + self.assertEqual(eitem['merge_state'], 'merged') + self.assertEqual(eitem['reactions_total_count'], 1) + self.assertEqual(eitem['reactions'][0]['type'], 'rocket') + self.assertEqual(eitem['reactions'][0]['count'], 1) + + item = self.items[3] + eitem = enrich_backend.get_rich_item(item) + self.assertEqual(item['category'], 'issue') + self.assertEqual(eitem['issue_state'], 'closed') + self.assertEqual(eitem['author_name'], 'Shashank Priyadarshi') + self.assertIsNone(eitem['author_domain']) + self.assertIsNone(eitem['assignee_domain']) + self.assertEqual(eitem['reactions_total_count'], 0) + self.assertEqual(eitem['reactions'], []) + self.assertEqual(eitem['time_to_first_attention'], 13.07) + self.assertEqual(eitem['time_to_close_days'], eitem['time_open_days']) + + item = self.items[4] + eitem = enrich_backend.get_rich_item(item) + self.assertEqual(item['category'], 'merge_request') + self.assertEqual(eitem['merge_state'], 'merged') + self.assertEqual(eitem['author_name'], 'Vaishnav') + self.assertIsNone(eitem['author_domain']) + self.assertIsNone(eitem['merge_author_domain']) + self.assertEqual(eitem['num_versions'], 1) + self.assertEqual(eitem['num_merge_comments'], 3) + self.assertEqual(eitem['time_to_merge_request_response'], 0) + + def test_enrich_repo_labels(self): + """Test whether the field REPO_LABELS is present in the enriched items""" + + self._test_raw_to_enrich() + enrich_backend = self.connectors[self.connector][2]() + + for item in self.items: + eitem = enrich_backend.get_rich_item(item) + self.assertIn(REPO_LABELS, eitem) + + def test_raw_to_enrich_sorting_hat(self): + """Test enrich with SortingHat""" + + result = self._test_raw_to_enrich(sortinghat=True) + self.assertGreater(result['raw'], 0) + self.assertGreater(result['enrich'], 0) + # self.assertEqual(result['raw'], result['enrich']) + + enrich_backend = self.connectors[self.connector][2]() + + url = self.es_con + "/" + self.enrich_index + "/_search" + response = enrich_backend.requests.get(url, verify=False).json() + for hit in response['hits']['hits']: + source = hit['_source'] + if 'author_uuid' in source: + self.assertIn('author_domain', source) + self.assertIn('author_gender', source) + self.assertIn('author_gender_acc', source) + self.assertIn('author_org_name', source) + self.assertIn('author_bot', source) + self.assertIn('author_multi_org_names', source) + + def test_raw_to_enrich_projects(self): + """Test enrich with Projects""" + + result = self._test_raw_to_enrich(projects=True) + # ... ? + + def test_refresh_identities(self): + """Test refresh identities""" + + result = self._test_refresh_identities() + # ... ? + + def test_refresh_project(self): + """Test refresh project field for all sources""" + + result = self._test_refresh_project() + # ... ? + + def test_perceval_params(self): + """Test the extraction of perceval params from an URL""" + + url = "https://gitlab.com/amfoss/cms-mobile" + expected_params = [ + 'amfoss', 'cms-mobile' + ] + self.assertListEqual(GitLabOcean.get_perceval_params_from_url(url), expected_params) + + def test_copy_raw_fields(self): + """Test copied raw fields""" + + self._test_raw_to_enrich() + enrich_backend = self.connectors[self.connector][2]() + + for item in self.items: + eitem = enrich_backend.get_rich_item(item) + for attribute in enrich_backend.RAW_FIELDS_COPY: + if attribute in item: + self.assertEqual(item[attribute], eitem[attribute]) + else: + self.assertIsNone(eitem[attribute]) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') + logging.getLogger("urllib3").setLevel(logging.WARNING) + logging.getLogger("requests").setLevel(logging.WARNING) + unittest.main(warnings='ignore')