Skip to content

Split the platform user from the Person contributor agent. - #686

Open
GianlucaFicarelli wants to merge 13 commits into
mainfrom
user
Open

Split the platform user from the Person contributor agent.#686
GianlucaFicarelli wants to merge 13 commits into
mainfrom
user

Conversation

@GianlucaFicarelli

@GianlucaFicarelli GianlucaFicarelli commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Introduce a dedicated PlatformUser table (keyed by Keycloak sub_id) separate from the Person agent.
created_by_id/updated_by_id on all entities now point to platform_user.id instead of agent.id.
Person remains an Agent subclass for use as a contributor (Contribution.agent_id) but no longer carries sub_id.

Previously Person served a dual role: contributor in Contribution records and platform user identified by sub_id.

Changes

Model

  • New PlatformUser table with id = Keycloak sub_id (UUID PK), given_name, family_name, pref_label
  • Identifiable.created_by_id / updated_by_id now FK to platform_user.id instead of agent.id
  • sub_id removed from Person

App

  • app/schemas/platform_user.py: new PlatformUserRead, NestedPlatformUserRead schemas
  • app/schemas/identifiable.py: created_by / updated_by typed as NestedPlatformUserRead
  • app/queries/utils.py: get_or_create_user returns a PlatformUser (no longer a Person)
  • app/filters/person.py: CreatorFilterMixin uses a new NestedPlatformUserFilter (model=PlatformUser)
  • All service files: created_by/updated_by aliases switched from Person to PlatformUser

Migration

  • Creates platform_user table, populates from existing person rows, remaps all
    created_by_id/updated_by_id FKs across 26 tables from agent.id to person.sub_id

Tests

  • Added test_get_or_create_user and test_get_user tests in test_db_utils.py

Breaking changes

  1. The search fields available in the filters have been renamed:
  • created_by__sub_id -> created_by__id
  • updated_by__sub_id -> updated_by__id
  • created_by__sub_id__in -> created_by__id_in
  • updated_by__sub_id__in -> updated_by__id_in

For backward compatibility, the old keys can still be used to allow the clients to adapt for the change, but they are deprecated and will be removed later.

  1. In any response that includes "created_by/updated_by", the payload doesn't contain anymore: type, orcid, sub_id. The value of sub_id is now in id. In some cases sub_id was null, and it was replaced by an existing subject id when possible, or a randomly generated id when not possible (for reference, see the migration file in the PR for the mapping).

Before:

      "created_by": {
        "creation_date": "2025-07-01T08:31:07.778464Z",
        "update_date": "2025-07-01T08:31:07.778464Z",
        "id": "75443b93-52d3-48c1-9bd9-30c66dceb6d9",
        "given_name": "Name",
        "family_name": "Surname",
        "pref_label": "Name Surname",
        "type": "person",
        "sub_id": "63fb2ff4-7631-4355-b00a-5a94c3c42f47",
        "orcid": null
      },

After:

      "created_by": {
        "creation_date": "2025-07-01T08:31:07.778464Z",
        "update_date": "2025-07-01T08:31:07.778464Z",
        "given_name": "Name",
        "family_name": "Surname",
        "pref_label": "Name Surname",
        "id": "63fb2ff4-7631-4355-b00a-5a94c3c42f47"
      },
  1. In the response from the get_many endpoints when including facets, the type of created_by and updated_by has changed from "person" to created_by and updated_by. The ids in the payload are the subject ids, not the agent ids anymore.
    The change on the type can be discussed further if needed, depending on how it's used by the client.
    Note that the facets for contribution are unchanged.

Before:

    "created_by": [
      {
        "id": "75443b93-52d3-48c1-9bd9-30c66dceb6d9",
        "label": "Name Surname",
        "count": 2285,
        "type": "person"
      },

After:

    "created_by": [
      {
        "id": "63fb2ff4-7631-4355-b00a-5a94c3c42f47",
        "label": "Name Surname",
        "count": 2285,
        "type": "created_by"
      },
  1. There aren't CRUD endpoints for "platform_user", since it's always created internally. If needed they can be added.

Fix #685

Introduce a dedicated User table (keyed by Keycloak sub_id) separate from
the Person agent. created_by_id/updated_by_id on all entities now point to
user.id instead of agent.id. Person remains an Agent subclass for use as a
contributor (Contribution.agent_id) but no longer carries sub_id.
@GianlucaFicarelli GianlucaFicarelli self-assigned this Jul 28, 2026
@GianlucaFicarelli GianlucaFicarelli changed the title Splits the platform user from the Person contributor agent. Split the platform user from the Person contributor agent. Jul 28, 2026
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
pytest 97.84% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
app/db/model.py 99.14% <100.00%> (+<0.01%) ⬆️
app/filters/activity.py 100.00% <100.00%> (ø)
app/filters/classification.py 100.00% <100.00%> (ø)
app/filters/consortium.py 100.00% <100.00%> (ø)
app/filters/contribution.py 100.00% <100.00%> (ø)
app/filters/derivation.py 100.00% <100.00%> (ø)
app/filters/entity.py 100.00% <100.00%> (ø)
app/filters/external_url.py 100.00% <100.00%> (ø)
app/filters/ion_channel.py 100.00% <100.00%> (ø)
app/filters/measurement_label.py 100.00% <100.00%> (ø)
... and 77 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread app/service/brain_atlas_region.py Outdated
@GianlucaFicarelli GianlucaFicarelli changed the title Split the platform user from the Person contributor agent. WIP Split the platform user from the Person contributor agent. Jul 29, 2026
@GianlucaFicarelli
GianlucaFicarelli marked this pull request as ready for review July 29, 2026 16:11
@GianlucaFicarelli GianlucaFicarelli changed the title WIP Split the platform user from the Person contributor agent. plit the platform user from the Person contributor agent. Jul 29, 2026
@GianlucaFicarelli GianlucaFicarelli changed the title plit the platform user from the Person contributor agent. Split the platform user from the Person contributor agent. Jul 29, 2026
@GianlucaFicarelli

Copy link
Copy Markdown
Collaborator Author

cc @bilalesi @pgetta @jankrepl since the changes in review can affect some functionalities related to the filter by created_by/updated_by.
See the breaking changes in the description, that we can discuss more in detail also offline if needed.

* origin/main:
  Change AdminContextDep to UserContextDep for Person, Organization, Consortium create (#688) (#690)
  Strict update schemas: forbid extras and merge exclusions (#687)
Comment thread app/db/model.py Outdated
Comment on lines +129 to +130
given_name: Mapped[str | None]
family_name: Mapped[str | None]

@eleftherioszisis eleftherioszisis Jul 30, 2026

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.

Do we need these for the platform users? I suppose with service accounts etc. it might be harder to conform to this schema.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

given_name and family_name aren't strictly needed for entitycore, but they are populated from the existing records, that in turn come from keycloak.
If we remove them, and keep only pref_label, then they are removed also from created_by and updated_by in the returned payloads.
Maybe @bilalesi can confirm if the UI expects those fields or not?

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.

Gianluca, for created_by/updated_by we only read pref_label - given_name and family_name aren't used anywhere in core-web-app, so dropping them from PlatformUser is fine on our side.

One thing worth keeping separate though: for contributions we do rely on Person.given_name/family_name. We build the contributor display name from the given/family pair and fall back to pref_label, and we sort contributors by family name:

So as long as they stay on Person as the contributor agent, removing them from PlatformUser doesn't affect us.

* origin/main:
  Move agent rules source to AGENTS.md and sync to Amazon Q and Claude. (#691)
@pgetta

pgetta commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Gianluca, I went through the breaking changes against core-web-app and none of them affect us, so no need to hold the merge for the frontend.

Concretely, the only ownership constraint we send is created_by__pref_label__in, the only sort is order_by=created_by__pref_label, and the only field we read from created_by/updated_by is pref_label. The "Created by" facet checklist also builds its filter value from the facet label rather than the id, so the id remapping in the migration doesn't invalidate any saved filter or URL state on our side either.

On the facet type change from person to created_by/updated_by - we don't branch on it for those two facets, so whichever reads better for you works for us. We do rely on the contribution facets keeping type: "person" (transformers.ts#L143), so I'm glad those stay unchanged.

Btw, one thing this unlocks for us. Filtering by display name is fragile in both directions: two different users sharing a pref_label collapse into a single option, and the same user with two spellings shows up as two. Your migration mapping has examples of the second case - Aurelien / Aurélien Jaquier and Ilkan Kilic / Kiliç now share one subject id but keep separate labels. Since created_by__id is the subject id now and the facet returns it, we can finally move that filter to ids. Not a call for action for this PR, just a note that we'd like to keep created_by__id__in and the facet id around.

I've prepared the type-side adaptation on our end already: openbraininstitute/core-web-app#1847.

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.

Split Person from User

3 participants