Beispiel #1
0
def test_store_issue_stores_file(regular_issue, test_repo):
    ih = IssueHandler()
    ih.store_issue(regular_issue, None, True)
    result = ih.get_issue_from_issue_id(regular_issue.id)
    regular_issue.id = "ISSUE-11"  # 11 because tracker is mocked to have 10 entries

    assert regular_issue == result
def test_create_resolution(issue_1: Issue, issue_2: Issue, monkeypatch, first_repo: git.Repo):
    handler = IssueHandler()

    issue_1_path = handler.get_issue_path(issue_1)
    issue_2_path = handler.get_issue_path(issue_2)
    issues = [issue_1, issue_2]

    os.chdir(first_repo.working_dir)
    monkeypatch.setattr("git_issue.git_manager.GitManager.get_choice_from_user", lambda x, y: True)
    monkeypatch.setattr("builtins.input", lambda x: 'Y')

    uuids = [UUIDTrack(issue_1.uuid, issue_1.id), UUIDTrack(issue_2.uuid, issue_2.id)]

    resolution = CreateResolutionTool(issues, Tracker(len(uuids), uuids))

    resolution.resolve()
    GitManager().commit("-m", "merge conflict resolution")

    assert issue_1_path.exists()
    assert issue_2_path.exists()

    result_1 = handler.get_issue_from_issue_id(issues[0].id)
    result_2 = handler.get_issue_from_issue_id(issues[1].id)

    assert issues[0] == result_1
    assert issues[1] == result_2
Beispiel #3
0
def test_store_issue_has_correct_path(regular_issue, test_repo):
    root = f"{test_repo.working_dir}/issue"
    ih = IssueHandler()
    ih.store_issue(regular_issue, None)
    gm = GitManager()
    gm.load_issue_branch()
    assert Path(f"{root}/{regular_issue.id}/issue.json").exists()
Beispiel #4
0
    def get(self, id):
        handler = IssueHandler()
        if (not handler.does_issue_exist(id)):
            abort(HTTPStatus.NOT_FOUND)

        issue = handler.get_issue_from_issue_id(id)
        result = to_payload(GitUser(), issue, IssueSchema)
        return result.data
Beispiel #5
0
    def get(self, args):
        page = args.get("page", 1)
        limit = 1000  # args.get("limit", 1000)

        handler = IssueHandler()
        issues, count = handler.get_issue_range(page, limit)
        response = IssueList(count, issues)

        result = to_payload(GitUser(), response, IssueListSchema)
        return result.data
def test_divergence_resolution(issue_3, first_repo, monkeypatch):
    os.chdir(first_repo.working_dir)
    monkeypatch.setattr("builtins.input", lambda x: 'Y')

    resolution = DivergenceResolutionTool([issue_3])
    resolution.resolve()

    handler = IssueHandler()
    result = handler.get_issue_from_issue_id(issue_3.id)

    assert issue_3 == result
    def resolve(self):
        gm = GitManager()
        gm.load_issue_branch()
        paths = []
        handler = IssueHandler()

        for issue in self.resolved_issues:
            file_path = handler.get_issue_path(issue)
            JsonConvert.ToFile(issue, file_path)
            paths.append(str(file_path))

        repo = gm.obtain_repo()
        for path in paths:
            repo.git.add(path)
Beispiel #8
0
def confirm_operation(issue, operation):
    print("Operation will result in the following issue:\n")
    handler = IssueHandler()
    handler.display_issue(issue)
    create = input(f"\nConfirm (Y/N): ").capitalize()

    while create != "Y" and create != "YES" and create != "N" and create != "NO":
        create = input("\nInvalid input, please try again (Y/N): ").capitalize()

    if create == "Y" or create == "YES":
        operation()
        print(f"Operation successful.")

    else:
        print("Operation cancelled.")
Beispiel #9
0
    def post(self):
        json = request.get_json()

        issue = Issue()
        issue.summary = json.get("summary")
        issue.description = json.get("description")
        reporter = json.get("reporter")
        assignee = json.get("assignee")
        issue.reporter = GitUser(email=reporter.get(
            "email")) if reporter is not None else GitUser()
        issue.assignee = GitUser(
            email=assignee.get("email")) if assignee is not None else None

        issue.subscribers.append(issue.reporter)
        if issue.assignee is not None and issue.assignee not in issue.subscribers:
            issue.subscribers.append(issue.assignee)

        gm = GitManager()
        handler = gm.perform_git_workflow(lambda: IssueHandler())
        created_issue = handler.store_issue(issue,
                                            "create",
                                            generate_id=True,
                                            store_tracker=True)
        result = to_payload(GitUser(), issue, IssueSchema)

        return result.data, HTTPStatus.CREATED, {
            'location': f'issues/${created_issue.id}'
        }
Beispiel #10
0
def comment(args):
    ih = GitManager().perform_git_workflow(lambda: IssueHandler())
    handler = GitManager().perform_git_workflow(lambda: CommentHandler(ih.get_issue_folder_path(args.issue), args.issue))

    if args.comment:
        c = Comment(args.comment)
        handler.add_comment(c)
    else:
        comments = GitManager().perform_git_workflow(lambda: handler.get_comment_range(10000, 0))
        for c in comments:
            print(f"Email: {c.user.email}\tDate: {c.date}\n\t{c.comment}\n")
Beispiel #11
0
def show(args):
    if args.issue is not None:
        handler = GitManager().perform_git_workflow(lambda: IssueHandler())
        issue = handler.get_issue_from_issue_id(args.issue)

        if issue == None:
            print(f"Issue with ID {args.issue} was not found.")
        else:
            handler.display_issue(issue)
    else:
        list(args)
Beispiel #12
0
def change_status(issue_id, status):
    handler = GitManager().perform_git_workflow(lambda: IssueHandler())

    if not handler.does_issue_exist(issue_id):
        print("Error: Issue does not exist")
        return

    issue = handler.get_issue_from_issue_id(issue_id)
    original_status = issue.status
    issue.status = status
    handler.store_issue(issue, "edit")
    print(f"Status of {issue.id} changed from '{original_status}' to '{issue.status}'")
Beispiel #13
0
    def post(self, id):
        ih = IssueHandler()

        if (not ih.does_issue_exist(id)):
            raise BadRequest(f"Issue with id {id} does not exist.")

        comment = request.get_json().get("comment")

        if (comment is None):
            raise BadRequest(f"No comment given.")

        comment = Comment(comment)
        gm = GitManager()
        path = gm.perform_git_workflow(lambda: ih.get_issue_folder_path(id))
        handler = CommentHandler(path, id)
        created_comment = handler.add_comment(comment)

        schema = CommentSchema()
        result = schema.dump(comment)

        return result.data, HTTPStatus.CREATED
Beispiel #14
0
    def put(self, id):
        edit_schema = IssueSchema(only=tuple(IssueSchema.issue_fields.keys()))
        parsed_data = edit_schema.load(request.get_json())

        if (len(parsed_data.errors.items()) > 0):
            return f"Errors encountered with the request: {parsed_data.errors}", 416

        updated_issue = parsed_data.data

        issue = None
        httpStatus: HTTPStatus = None
        headers = {}

        handler = IssueHandler()
        if (not handler.does_issue_exist(id)):
            issue = handler.store_issue(updated_issue, "create", True)

            hash = hashlib.sha256(
                b"{regular_schema.dump(issue).data}").hexdigest()
            print(f"Hash: {hash}")
            headers["ETag"] = hash
            httpStatus = HTTPStatus.CREATED

        else:
            current_issue = handler.get_issue_from_issue_id(id)

            if (updated_issue.id != id):
                return "Given issue ID does not match url", 416

            updated_issue.date = current_issue.date  # Ensure date NEVER changes
            issue = handler.store_issue(updated_issue, "edit")

        result = to_payload(GitUser(), issue, IssueSchema)

        return result.data, HTTPStatus.OK
Beispiel #15
0
    def get(self, args, id):
        issue_handler = IssueHandler()
        if not issue_handler.does_issue_exist(id):
            raise BadRequest(f"Issue with id {id} does not exist.")

        page = args.get("page", 1)
        limit = 1000  # args.get("limit", 10)

        # Each page is limit amount of comments, therefore start_pos
        # is the limit of comments per page, times by the page number
        start_pos = limit * (page - 1)
        gm = GitManager()

        def action():
            path = issue_handler.get_issue_folder_path(id)
            comment_handler = CommentHandler(path, id)
            return comment_handler.get_comment_range(limit, start_pos)

        comments = gm.perform_git_workflow(action)
        schema = CommentSchema()
        result = schema.dump(comments, many=True)

        return result.data
def test_produce_divergence_resolver(issue_1, issue_2, issue_3, first_repo):
    resolved_issues = [issue_1, issue_2, issue_3]
    resolved_tracker = Tracker()

    for issue in resolved_issues:
        resolved_tracker.track_or_update_uuid(issue.uuid, issue.id)

    issue_2_json = JsonConvert.ToJSON(issue_2)
    issue_2_copy = JsonConvert.FromJSON(issue_2_json)
    issue_2_copy.uuid = 53368803138698180295652887974160049016

    conflict = ConflictInfo(IssueHandler().get_issue_path(issue_2), [issue_2, issue_2, issue_2_copy])

    merger = GitMerge(first_repo)
    resolver = merger.produce_create_edit_divergence_resolver([conflict], resolved_issues, resolved_tracker)
    assert resolved_issues == resolver.resolved_conflicts and resolved_tracker == resolver.resolved_tracker
    assert [(issue_2, issue_2_copy)] == resolver.diverged_issues
Beispiel #17
0
def edit(args):
    handler = GitManager().perform_git_workflow(lambda: IssueHandler())
    if not handler.does_issue_exist(args.issue):
        print("Error: Issue does not exist")
        return

    issue = handler.get_issue_from_issue_id(args.issue)

    if (issue == None):
        print("There was a problem ")

    print("Issue before editing:")
    handler.display_issue(issue)

    issue.summary = args.summary if args.summary != None else issue.summary
    issue.description = args.description if args.description != None else issue.description
    issue.assignee = GitUser(email=args.assignee) if args.assignee != None else issue.assignee
    issue.reporter = GitUser(email=args.reporter) if args.reporter != None else issue.reporter
    issue.status = args.status if args.status != None else issue.status

    print()
    confirm_operation(issue, lambda: handler.store_issue(issue, "edit"))
Beispiel #18
0
    def generate_resolution(self):
        matching_issues: (Issue, Issue) = []
        diverged_issues = self.diverged_issues
        resolved_issues = []

        for resolved in self.resolved_conflicts:
            found = False
            diverged_index = 0

            for stage_2, stage_3 in diverged_issues:
                match = None

                if stage_2.uuid == resolved.uuid and stage_2.id != resolved.id:
                    match = stage_2
                    resolved_issues.append(stage_3)
                elif stage_3.uuid == resolved.uuid and stage_3.id != resolved.id:
                    match = stage_3
                    resolved_issues.append(stage_2)

                if match is not None:
                    found = True
                    matching_issues.append((match, resolved))
                    break
                diverged_index += 1

            if found:  # Remove the found issue from the list so we don't need to examine it again
                del diverged_issues[diverged_index]

        handler = IssueHandler(self.resolved_tracker)

        for stage_2, stage_3 in diverged_issues:
            loaded_stage_2 = None
            loaded_stage_3 = None

            # If it fails then it means the JSON is garbled with Git merge stuff. That or it's just messed up...
            # Either way, it's a fail.
            try:
                loaded_stage_2 = handler.get_issue_from_uuid(stage_2.uuid)
            except JSONDecodeError:
                pass
            try:
                loaded_stage_3 = handler.get_issue_from_uuid(stage_3.uuid)
            except JSONDecodeError:
                pass

            if loaded_stage_2 is not None and loaded_stage_2.id != stage_2.id:
                matching_issues.append((stage_2, loaded_stage_2))
                resolved_issues.append(stage_3)
            elif loaded_stage_3 is not None and loaded_stage_3.id != stage_3.id:
                matching_issues.append((stage_3, loaded_stage_3))
                resolved_issues.append(stage_2)
            else:
                raise DivergenceMatchMissingError(
                    stage_2, stage_3,
                    "Failed to discern the stage that is diverged")

        if len(matching_issues) > 0:
            print("One or more create-edit divergencies have been identified. This is when an issue on "\
                "one branch has been edited, after it has been resolved as a create conflict "\
                "on another branch that is being merged with the current branch.")

            for diverged, match in matching_issues:
                resolved = self._get_edit_resolution(diverged, match)
                resolved_issues.append(resolved)

        resolution = DivergenceResolutionTool(resolved_issues)

        return resolution
Beispiel #19
0
    def generate_resolution(self):
        """
            Resolves conflicts by re-ordering the issues based on their creation date.
            The conflicts are resolved by giving the oldest issue the first conflicting issue ID.

            Returns a tuple containing the list of resolved conflicts, and an updated tracker.
            These resolved conflicts are NOT stored to fie by this method. This is left as a job for the consumer.
        """

        # Create copies of data to avoid mutating them as a side affect
        conflicts = []

        for info in self.conflicts:
            for issue in info.conflicts:
                conflicts.append(issue)

        if self.tracker is None:
            tracker = Tracker.obtain_tracker()
        else:
            tracker = Tracker(self.tracker.issue_count,
                              self.tracker.tracked_uuids.copy())

        complete = False
        handler = IssueHandler(tracker)
        ids = [issue.id for issue in conflicts]

        while not complete:
            to_add = []

            for issue in conflicts:
                next_id = handler.next_issue_id(issue.id)
                while next_id in ids:
                    next_id = handler.next_issue_id(next_id)

                if next_id not in ids:
                    if handler.does_issue_exist(next_id):
                        missing = handler.get_issue_from_issue_id(next_id)
                        to_add.append(missing)

                    ids.append(next_id)

            for issue in to_add:
                conflicts.append(issue)
                ids.append(issue.id)

            complete = len(to_add) == 0

        conflicts.sort(key=lambda x: x.date)
        sorted_ids = list(set(ids))
        sorted_ids.sort()

        assert len(sorted_ids) >= len(conflicts)

        index = 0
        for issue in conflicts:
            new_id = sorted_ids[index]
            issue.id = new_id
            tracker.track_or_update_uuid(issue.uuid, new_id)
            index += 1

        return CreateResolutionTool(conflicts, tracker)
def test_unmerged_conflicts(issue_1: Issue, issue_2: Issue, issue_3: Issue, first_repo: git.Repo, second_repo: git.Repo,
                            monkeypatch):
    # Create, Create-Edit-Divergence, Comment-Index, Manual
    issue_2_json = JsonConvert.ToJSON(issue_2)
    issue_2_copy = JsonConvert.FromJSON(issue_2_json)
    issue_2_copy.summary = "Edited Summary"

    monkeypatch.setattr("git_issue.git_manager.GitManager.get_choice_from_user", lambda x, y: True)
    monkeypatch.setattr("builtins.input", lambda x: 'Y')

    def merge(repo):
        try:
            repo.git.checkout("issue")
            repo.git.pull("--allow-unrelated-histories", "other", GitManager.ISSUE_BRANCH)
        except:
            pass

        merger = GitMerge(repo)
        return merger.parse_unmerged_conflicts()

    # Set up first repo for Create conflict
    os.chdir(first_repo.working_dir)
    handler = IssueHandler()
    handler.store_issue(issue_2, "test", generate_id=True)

    # Set up second repo for Create conflict
    os.chdir(second_repo.working_dir)
    handler = IssueHandler()
    handler.store_issue(issue_1, "test", generate_id=True)

    result = merge(second_repo)
    expected = [ConflictInfo("ISSUE-1/issue.json", [issue_1, issue_2])]
    assert expected == result
    GitSynchronizer().merge(GitMerge(second_repo))

    # Set up first repo for divergence conflict
    os.chdir(first_repo.working_dir)
    handler = IssueHandler()
    issue_2_copy.id = issue_1.id
    handler.store_issue(issue_2_copy, "test")

    os.chdir(second_repo.working_dir)
    result = merge(second_repo)
    expected = [ConflictInfo("ISSUE-1/issue.json", [issue_2, issue_1, issue_2_copy])]
    assert expected == result
    GitSynchronizer().merge(GitMerge(second_repo))


    # Comment
    os.chdir(first_repo.working_dir)
    handler = IssueHandler()
    comment_handler = CommentHandler(handler.get_issue_path(issue_1), issue_1.id)
    first_comment = Comment("first repo")
    first_entry = comment_handler.add_comment(first_comment)

    # Comment
    os.chdir(second_repo.working_dir)
    handler = IssueHandler()
    comment_handler = CommentHandler(handler.get_issue_path(issue_1), issue_1.id)
    second_comment = Comment("second repo")
    second_entry = comment_handler.add_comment(second_comment)

    result = merge(second_repo)
    expected = [ConflictInfo("ISSUE-1/index.json", [Index([second_entry]), Index([first_entry])])]

    assert expected == result
    GitSynchronizer().merge(GitMerge(second_repo))

    # Edit conflict
    issue_1.id = "ISSUE-10" # Just so we don't need to work out the ID
    issue_1_json = JsonConvert.ToJSON(issue_1)
    issue_1_first_repo = JsonConvert.FromJSON(issue_1_json)
    issue_1_second_repo = JsonConvert.FromJSON(issue_1_json)
    issue_1_first_repo.summary = "First Repo"
    issue_1_second_repo.summary = "Second Repo"

    # Set up both repos with the issue that will be used as a conflict
    os.chdir(first_repo.working_dir)
    handler = IssueHandler()
    handler.store_issue(issue_1, "test")
    os.chdir(second_repo.working_dir)
    result = merge(second_repo)

    # Edit first repo
    os.chdir(first_repo.working_dir)
    handler.store_issue(issue_1_first_repo, "test")

    # Edit second repo
    os.chdir(second_repo.working_dir)
    handler = IssueHandler()
    handler.store_issue(issue_1_second_repo, "issue 10 edit")
    result = merge(second_repo)

    expected = [ConflictInfo("ISSUE-10/issue.json", [issue_1, issue_1_second_repo, issue_1_first_repo])]
    assert expected == result
    GitSynchronizer().merge(GitMerge(second_repo))
Beispiel #21
0
 def operation():
     handler = GitManager().perform_git_workflow(lambda: IssueHandler())
     new_issue = handler.store_issue(issue, "creation", True, True)
     print(f"ID of newly created issue: {new_issue.id}")
Beispiel #22
0
def list(args):
    handler = GitManager().perform_git_workflow(lambda: IssueHandler())
    issues = issue_handler.get_all_issues()
    for i in issues:
        handler.display_issue(i)
        print()