Skip to content

fix(api): deprecate related.id in favour of related.ids#486

Merged
cccs-mdr merged 5 commits into
developfrom
deprecate-related-id
Jul 20, 2026
Merged

fix(api): deprecate related.id in favour of related.ids#486
cccs-mdr merged 5 commits into
developfrom
deprecate-related-id

Conversation

@cccs-mdr

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings July 13, 2026 14:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the API ODM’s ECS Related model to deprecate related.id in favor of related.ids, while preserving backwards compatibility by copying a provided id into ids during model construction.

Changes:

  • Mark Related.id as deprecated with a migration hint to use Related.ids.
  • Add Related.__init__ logic that merges id into ids (avoiding duplicates).
  • Add unit tests validating idids migration, including propagation through Hit construction.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
api/howler/odm/models/ecs/related.py Deprecates id and adds constructor-time migration from id to ids.
api/test/unit/odm/test_related.py Adds unit tests for Related migration behavior and parent ODM propagation.

Comment thread api/howler/odm/models/ecs/related.py
Comment thread api/test/unit/odm/test_related.py
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Static Badge

Howler API - Coverage Results

Static Badge Static Badge

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

No lines with coverage information in this diff.

Full Coverage Report

Expand
Name                                            Stmts   Miss Branch BrPart  Cover
---------------------------------------------------------------------------------
howler/__init__.py                                  0      0      0      0   100%
howler/actions/__init__.py                         83      2     36      3    96%
howler/actions/add_label.py                        30      4      6      2    83%
howler/actions/add_to_bundle.py                    50      6     14      3    83%
howler/actions/change_field.py                     18      9      2      0    45%
howler/actions/demote.py                           35     18      8      0    40%
howler/actions/example_plugin.py                   13      8      2      0    33%
howler/actions/prioritization.py                   24     12      2      0    46%
howler/actions/promote.py                          35     18      8      0    40%
howler/actions/remove_from_bundle.py               47      6     14      5    79%
howler/actions/remove_label.py                     30      3      6      2    86%
howler/actions/transition.py                       66      4     24      4    91%
howler/api/__init__.py                            121     48     26      2    56%
howler/api/base.py                                 36      0     12      0   100%
howler/api/socket.py                               69      9     16      6    82%
howler/api/v1/__init__.py                          38      3     14      2    90%
howler/api/v1/action.py                           132     31     32      8    71%
howler/api/v1/analytic.py                         307     87     98     33    66%
howler/api/v1/auth.py                             164     25     56     16    80%
howler/api/v1/clue.py                              47     14     12      0    66%
howler/api/v1/configs.py                           14      0      0      0   100%
howler/api/v1/dossier.py                           96     27     10      4    69%
howler/api/v1/help.py                              13      0      0      0   100%
howler/api/v1/hit.py                              482     69    160     42    82%
howler/api/v1/notebook.py                          33      5      6      1    85%
howler/api/v1/overview.py                          84     10     18      6    84%
howler/api/v1/search.py                           303     80     90     17    71%
howler/api/v1/template.py                          93     12     26      8    83%
howler/api/v1/tool.py                             112      9     48      5    90%
howler/api/v1/user.py                             182     55     58     22    63%
howler/api/v1/utils/__init__.py                     0      0      0      0   100%
howler/api/v1/utils/etag.py                        23      0      8      1    97%
howler/api/v1/utils/params.py                      30      3     10      1    85%
howler/api/v1/view.py                             134     26     36     12    78%
howler/app.py                                     129     15     36     12    82%
howler/common/__init__.py                           0      0      0      0   100%
howler/common/classification.py                   597     78    342     73    83%
howler/common/exceptions.py                        74      8      2      0    89%
howler/common/loader.py                            87     24     40     11    66%
howler/common/logging/__init__.py                 141     83     64      7    36%
howler/common/logging/audit.py                     44      5      8      4    83%
howler/common/logging/format.py                    14      2      0      0    86%
howler/common/net.py                               55      2     28      2    95%
howler/common/net_static.py                         1      0      0      0   100%
howler/common/random_user.py                       10      0      2      0   100%
howler/common/swagger.py                           48     12     18      4    70%
howler/config.py                                   28      0      0      0   100%
howler/cronjobs/__init__.py                        19      2      4      0    91%
howler/cronjobs/action_queue_worker.py             22      2      6      1    89%
howler/cronjobs/retention.py                       48     15     12      2    68%
howler/cronjobs/rules.py                          116      9     36      5    89%
howler/cronjobs/view_cleanup.py                    47      6     22      4    83%
howler/datastore/__init__.py                        0      0      0      0   100%
howler/datastore/bulk.py                           53     53     26      0     0%
howler/datastore/collection.py                   1283    254    572    114    77%
howler/datastore/constants.py                       7      0      0      0   100%
howler/datastore/exceptions.py                     23      1      0      0    96%
howler/datastore/howler_store.py                   74      8     14      2    89%
howler/datastore/operations.py                     45      5     16      5    84%
howler/datastore/schemas.py                         4      4      0      0     0%
howler/datastore/store.py                         155     36     36      8    73%
howler/datastore/support/__init__.py                0      0      0      0   100%
howler/datastore/support/build.py                 101     26     68     10    70%
howler/datastore/support/schemas.py                 4      0      0      0   100%
howler/datastore/types.py                          13      0      0      0   100%
howler/error.py                                    52     31     12      0    33%
howler/healthz.py                                  47     31     12      0    27%
howler/helper/__init__.py                           0      0      0      0   100%
howler/helper/azure.py                             20      0      8      0   100%
howler/helper/discover.py                          32     10     12      4    64%
howler/helper/hit.py                               66     17     32     12    68%
howler/helper/oauth.py                            155     68    100     19    52%
howler/helper/search.py                            20      1      2      1    91%
howler/helper/workflow.py                          47      3     22      3    91%
howler/odm/__init__.py                              1      0      0      0   100%
howler/odm/base.py                                902    165    408     44    78%
howler/odm/helper.py                              171     22     68      6    82%
howler/odm/howler_enum.py                          13      0      0      0   100%
howler/odm/randomizer.py                          219     44    120     18    79%
howler/plugins/__init__.py                         17      0      4      0   100%
howler/plugins/config.py                           78      0     40      0   100%
howler/remote/__init__.py                           0      0      0      0   100%
howler/remote/datatypes/__init__.py               104      2     40      2    97%
howler/remote/datatypes/counters.py                47      5     16      0    92%
howler/remote/datatypes/events.py                  35      1      6      3    90%
howler/remote/datatypes/hash.py                   114     17     22      7    82%
howler/remote/datatypes/lock.py                    19      0      2      0   100%
howler/remote/datatypes/queues/__init__.py          0      0      0      0   100%
howler/remote/datatypes/queues/comms.py            46     10     14      3    75%
howler/remote/datatypes/queues/multi.py            22      3      8      3    80%
howler/remote/datatypes/queues/named.py            66      6     24      6    87%
howler/remote/datatypes/queues/priority.py        107     20     38     12    75%
howler/remote/datatypes/set.py                     74     12      8      2    80%
howler/remote/datatypes/user_quota_tracker.py      28     10      4      0    56%
howler/security/__init__.py                       128     21     40     10    82%
howler/security/socket.py                          53     10     12      5    74%
howler/security/utils.py                           81     37     36      2    48%
howler/services/__init__.py                         0      0      0      0   100%
howler/services/action_service.py                 101      7     52      5    91%
howler/services/analytic_service.py                78     12     32      3    85%
howler/services/auth_service.py                   140     13     52     10    88%
howler/services/config_service.py                  50      8      8      2    83%
howler/services/dossier_service.py                 79      7     40      2    89%
howler/services/event_service.py                   43     13     18      3    64%
howler/services/hit_service.py                    258     12    108      8    93%
howler/services/jwt_service.py                     71     20     18      3    74%
howler/services/lucene_service.py                 157     12     48      8    90%
howler/services/notebook_service.py                58      3     18      3    92%
howler/services/overview_service.py                21      5      6      2    74%
howler/services/template_service.py                22      5      6      2    75%
howler/services/user_service.py                   150     32     66     17    72%
howler/telemetry.py                                33     28      6      0    13%
howler/utils/__init__.py                            0      0      0      0   100%
howler/utils/annotations.py                         0      0      0      0   100%
howler/utils/chunk.py                               8      0      2      0   100%
howler/utils/compat.py                             11      6      2      1    46%
howler/utils/constants.py                           3      0      0      0   100%
howler/utils/dict_utils.py                        123     12     86     11    87%
howler/utils/isotime.py                             8      1      2      1    80%
howler/utils/list_utils.py                          7      0      4      0   100%
howler/utils/lucene.py                             52      1     18      1    97%
howler/utils/path.py                               16     16      2      0     0%
howler/utils/socket_utils.py                       22      0     14      3    92%
howler/utils/str_utils.py                         115     21     38      4    80%
howler/utils/uid.py                                20      2      4      2    83%
---------------------------------------------------------------------------------
TOTAL                                           10596   2035   3970    722    77%

@cccs-mdr
cccs-mdr requested review from a team, cccs-hxp, cccs-mb and cccs-tsha July 14, 2026 11:58
cccs-mdr and others added 2 commits July 14, 2026 15:01
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@cccs-hxp cccs-hxp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me!

def __init__(self, data: dict = None, *args, **kwargs):
if data is not None and "id" in data and data["id"] is not None:
# Avoid mutating the caller-provided dict
data = dict(data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you re-construct the only potential list that you modify on line 60, making a shallow copy here works in the current __init__, but it might be safer to use deepcopy so that future changes don't end up modifying the provided dict by accident.

Comment thread api/howler/odm/models/ecs/related.py Outdated
Co-authored-by: cccs-hxp <xuan.pham@cyber.gc.ca>
@cccs-mdr
cccs-mdr merged commit 5e2c9bc into develop Jul 20, 2026
14 checks passed
@cccs-mdr
cccs-mdr deleted the deprecate-related-id branch July 20, 2026 13:30
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.

4 participants