Skip to content

Refactor: improve code quality and clean up technical debt #345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

cutekibry
Copy link
Contributor

@cutekibry cutekibry commented Apr 28, 2025

Description

This PR focuses on comprehensive code quality improvements, with the primary goal of eliminating all linter errors in /jira/*.py. The changes include date handling unification, type system enhancements, and code duplication removal.

Changes

1. Date Handling Unification

  • Consolidated all date-related functions into utils/date.py:
    • Adopted dateutil.parser.parse for more flexible date parsing
    • Changed return type to datetime | None for better type safety
    • Eliminated redundant <Mixin>._parse_date methods as they were unnecessary for LLM processing
    • Removed duplicate date formatting implementations across different mixins

2. Type System Improvements

  • Enhanced protocol.py:
    • Introduced new Protocol definitions for better interface segregation
    • Added @abstractmethod decorators to enforce implementation requirements
    • Removed problematic @runtime_checkable decorators (see Python docs)
  • Strengthened type system throughout the codebase:
    • Added comprehensive type hints
    • Implemented stricter type checking
    • Initialized .pylintrc to express the using of f-str in loggers explicitly to linter
  • Removed unsafe hasattr checks:
    • These checks were unreliable as they wouldn't automatically update with type/name changes

3. Code Duplication Elimination

  • Consolidated get_jira_field_ids implementations:
    • Removed duplicate implementations from EpicsMixin, IssuesMixin, and FieldsMixin
    • Centralized implementation in FieldsMixin using EpicsMixin's logic (JiraFetcher inherits EpicsMixin first)
    • Renamed to get_field_ids_to_epic for clarity
  • Consolidated get_epic_issues implementations:
    • Removed duplicate implementation in SearchMixin
    • Keep implementation in EpicsMixin unchanged
  • Refactored JiraClient._field_id:
    • Renamed to _field_ids_cache for better naming consistency
    • Modified type from dict[str, str] | None to list[dict[str, Any]] | None to match API structure

4. Test Suite Improvements

  • Updated test cases to reflect new implementations
  • Eliminated redundant test scenarios
  • Removed dangerous delattr usage in tests:
    • This practice was particularly risky as deleting attributes are not expected when linter performs type checks
    • Even with linter hints showing attribute existence, deleted attributes would cause runtime errors
    • This change prevents potential fatal issues in attribute access patterns

Critical Fixes Needed

  1. FieldsMixin.format_field_value server/DC compatibility (ref: 668784d):
    • Current issue: Cannot return {"name": value} for server/DC cases
    • Root cause: JiraFetcher always inherits from UsersMixin, making _get_account_id always available
    • Impact: Test expectations for server/DC scenarios cannot be met
    • Needs architectural review for proper server/DC support @sooperset
  2. test_jira_get_issue_link_types not passed

Testing and Validation

  • Unit tests added/updated
  • Integration tests passed
  • Manual checks performed

Checklist

  • Code follows project style guidelines (linting passes)
  • Tests added/updated for changes
  • All tests pass locally
  • Documentation updated (if needed)
  • Server/DC compatibility issues fixed

- Remove `@runtime_checkable` as unnecessary and incomplete (see note in
  https://docs.python.org/3/library/typing.html#typing.runtime_checkable)
- Add new protocols
- Add `@abstractmethod` to ensure the functions should always be
  implemented before instantiation of class
- Remove `/jira/utils.py` and `/utils/dates.py`
- Add `/utils/date.py`
- Only preserve `parse_date(date_str: str | None) -> datetime | None`
  - It is unnecessary for MCP Server to parse dates INTO different
    formats (e.g. human readable, ymd), since the format is unimportant
    to LLM.
  - Use `dateutil.parser.parse` as default: It provides much
    flexibility. We do not need to handle different formats of date by
    ourselves.
  - Change the return type from `str` to `datetime | None`: It would be
    better if models can use `datetime` to storage informations, as it
    is formatted explicitly. If we use `str`, we will have to guess the
    format of the date, or always call `parse_date` to ensure
    flexibility.
  - Remove other functions
    - `get_mixin_method`: It is not suggested to implement the same
      method in multiple mixins.
    - `escape_jql_string`: Unused.
- Rename `JiraClient._field_ids` to `_field_ids_cache` to ensure name
consistency
- Move the type hint to top of the class to make it clearer and better
  type hints
- Change `JiraClient._field_ids_cache` from `dict[str, str] | None` to
  `list[dict[str, Any]] | None`
  - Ensure consistency with API.
- Clean `get_jira_field_ids`
  - Remove it in `epics.py` and `fields.py`
  - Add it in `fields.py` using implementation in `epics.py`
    - `JiraFetcher` inherits `EpicsMixin` first so it is using the
      implementation from `EpicsMixin`.
  - Rename it to `FieldsMixin.get_field_ids_to_epic`
- Update protocols
- Remove `_parse_date` in `search.py`
- Remove `hasattr` check in `FieldsMixin.format_field_value`
  - `JiraFetcher` always inherits from `UsersMixin` so it is impossible
    for it to not have `_get_account_id`.
  - In this case, it is impossible to return `{"name": value}`.

FIXME: `format_field_value` needs to be fixed for server/DC cases
- Remove `_parse_date` and use `utils.parse_date` for consistency
- Add protocols
- Add necessary type checks (to fix linter errors)
- Remove unnecessary `hasattr`
- Move `JiraChangelog` to `/models/jira/common.py`
- Change `JiraChangelog.created` from `str` to `datetime | None`
- Remove `get_jira_field_ids` (which is renamed as
  `FieldsMixin.get_field_ids_to_epic`)
  - It may be not reasonable for all usages in `IssuesMixin` to call
    `get_field_ids_to_epic`, but in actual situation it does call
    `get_field_ids_to_epic`. See also: commit
    668784d
- Remove `_parse_date` and use `utils.parse_date` for consistency
- Remove unnecessary `hasattr`
- Add necessary type checks to fix linter errors
- Inherit mixins from `JiraFetcher` instead of mixins itself
  - This is because the mixins are highly coupling.
  - Expose it as mixin itself to type checker, to ensure there are no
    external callings to other mixins.
- Add type checks and type hints
- Remove unnecessary or duplicated tests
- Remove some `delattr` usages
  - It is not suggested to delete a class's attributes because if you do
    that, you will have to check ALL usages when you use ANY attributes
    because even IDE or linter hints you that the class has this
    attribute, the attribute still would be deleted and therefore not
    exists. This is fatal.
- Remove `delattr` since it is fatal
- Adapt to the rename of `get_jira_field_ids` to `get_field_ids_to_epic`
@cutekibry
Copy link
Contributor Author

test_jira_get_issue_link_types is not passed on my local machine but passed on Github CI/CD. Do we need to update the tests?

jira_client = <mcp_atlassian.jira.JiraFetcher object at 0x000001B20BB69B80>

    @pytest.mark.anyio
    async def test_jira_get_issue_link_types(jira_client: JiraFetcher) -> None:
        """Test retrieving issue link types from Jira."""
        # Initialize the LinksMixin
        links_client = LinksMixin(config=jira_client.config)

        # Get the issue link types
        link_types = links_client.get_issue_link_types()

        # Verify the response
        assert isinstance(link_types, list)
>       assert len(link_types) > 0
E       assert 0 > 0
E        +  where 0 = len([])

tests\test_real_api_validation.py:1453: AssertionError

@cutekibry cutekibry mentioned this pull request Apr 28, 2025
7 tasks
@cutekibry cutekibry changed the title refactor: improve code quality and clean up technical debt Refactor: improve code quality and clean up technical debt Apr 28, 2025
@sooperset
Copy link
Owner

sooperset commented Apr 30, 2025

@cutekibry I apologize for the delayed response. I've been quite busy recently and haven't had the chance to review this. I'll make sure to review and merge this PR either tonight or tomorrow. Thank you so much for your substantial contributions to the project!

@cutekibry
Copy link
Contributor Author

@cutekibry I apologize for the delayed response. I've been quite busy recently and haven't had the chance to review this. I'll make sure to review and merge this PR either tonight or tomorrow. Thank you so much for your substantial contributions to the project!

Never mind, it is not your main work so do not feel too much stress doing it. Even if you do not approve these PR, I still will merge them to my nightly build, that is to say, my work won't be interrupted.

I sincerely hope you are happy doing this project. Tomorrow is 5/1, wish you have a nice May Day.

sooperset added 2 commits May 1, 2025 15:35
- Replace the deprecated `createmeta` method with `issue_createmeta_fieldtypes` for fetching required fields.
- Implement logic to retrieve issue type ID using `get_project_issue_types`.
- Enhance error handling and logging for better debugging.
- Update unit tests to mock new API responses and validate functionality.
- Update the test to allow for an empty list as a valid response when no link types are configured.
- Refactor assertions to check the structure of the first element only if the list is not empty.
- Improve the docstring for clarity on expected behavior when issues are not found in attachment tests.
Copy link
Owner

@sooperset sooperset left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cutekibry

I've made a few test fixes and pushed them directly to your branch. All CI checks are now passing.

I fixed the test_jira_get_issue_link_types to handle cases where the result is empty (count of 0), and properly implemented the get_required_fields method to restore the tests.

The Server/DC compatibility issue with FieldsMixin.format_field_value that we discussed will be tracked as a separate issue (#355) and addressed in a follow-up PR.

Now that all tests are passing and the refactoring looks great, I'm going to merge this PR. Thank you again for your valuable contribution with this important refactoring work!

@sooperset sooperset merged commit 9b07222 into sooperset:main May 1, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants