Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/backend/src/api/routes/ingestion/integrity_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,13 @@ def _final_exists(lnk: IntegrityLink) -> bool:
items.append(item)

usernames = list({item.integrity_owner for item in items})
display_names = ConsoleService(get_settings().CONSOLE_INTERNAL_URL).fetch_users_by_usernames(
usernames
)
organizations = list({item.integrity_organization for item in items})
console = ConsoleService(get_settings().CONSOLE_INTERNAL_URL)
display_names = console.fetch_users_by_usernames(usernames)
organization_names = console.fetch_organizations_by_short_names(organizations)
for item in items:
item.owner_display_name = display_names.get(item.integrity_owner)
item.organization_display_name = organization_names.get(item.integrity_organization)

return IntegrityLinkListResponse(
items=items,
Expand Down
1 change: 1 addition & 0 deletions apps/backend/src/models/data_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class IntegrityLinkListItem(BaseModel):
public_access: PublicAccess = PublicAccess.UNCONFIGURED
has_final_table: bool = False
owner_display_name: str | None = None
organization_display_name: str | None = None

@field_validator("id", mode="before")
@classmethod
Expand Down
37 changes: 37 additions & 0 deletions apps/backend/tests/api/routes/test_integrity_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def mock_console_service():
"""Patch ConsoleService so tests don't hit the network; returns empty display names by default."""
with patch(_CONSOLE_PATCH) as mock_cls:
mock_cls.return_value.fetch_users_by_usernames.return_value = {}
mock_cls.return_value.fetch_organizations_by_short_names.return_value = {}
yield mock_cls


Expand Down Expand Up @@ -772,6 +773,42 @@ def test_owner_display_name_none_when_console_fails(

assert response.items[0].owner_display_name is None

@patch("src.api.routes.ingestion.integrity_links.logger")
def test_organization_display_name_populated_from_console(
self,
mock_logger: MagicMock,
mock_session: MagicMock,
mock_data_session: MagicMock,
mock_console_service: MagicMock,
) -> None:
"""organization_display_name is the console long name of the owning organization."""
link = IntegrityLink(
id=uuid4(),
integrity_title="Org Link",
integrity_owner="jdoe",
integrity_organization="testorg",
source_import_type=ImportType.URL,
staging_table_name="staging_org",
created_at=datetime.now(timezone.utc),
schedule_enabled=False,
)
self._setup_session(mock_session, [link])
self._setup_data_session(mock_data_session, ["staging_org"], [])
mock_console_service.return_value.fetch_organizations_by_short_names.return_value = {
"testorg": "Test Organization"
}

geo_ctx = self._create_geo_ctx("jdoe", {"IMPORT"})
response = list_integrity_links(
session=mock_session,
data_session=mock_data_session,
geo_ctx=geo_ctx,
group_ids=[],
offset=0,
)

assert response.items[0].organization_display_name == "Test Organization"

@patch("src.api.routes.ingestion.integrity_links.logger")
def test_next_offset_present_no_more(
self, mock_logger: MagicMock, mock_session: MagicMock, mock_data_session: MagicMock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface IntegrityLinkListItem {
integrity_title: string | null
last_retrieval_timestamp: string | null
metadata_id: string | null
organization_display_name?: string | null
owner_display_name?: string | null
preset_id?: RecurrencePreset | null
public_access?: PublicAccess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ <h1 class="text-2xl font-bold text-gray-2">
<div class="flex-1 pl-2 pr-4 text-sm font-semibold text-gray-2">
{{ 'integrityLinks.title' | translate }}
</div>
<div class="w-48 pr-4 text-sm font-semibold text-gray-2">
{{ 'integrityLinks.organization' | translate }}
</div>
<div class="w-48 pr-4 text-sm font-semibold text-gray-2">
{{ 'integrityLinks.owner' | translate }}
</div>
Expand Down Expand Up @@ -126,7 +129,18 @@ <h1 class="text-2xl font-bold text-gray-2">
</span>
}
</div>
<div class="w-48 pr-4 text-sm text-gray-2">
<div
class="w-48 truncate pr-4 text-sm text-gray-2"
[title]="
link.organization_display_name ?? link.integrity_organization
"
>
{{ link.organization_display_name ?? link.integrity_organization }}
</div>
<div
class="w-48 truncate pr-4 text-sm text-gray-2"
[title]="link.owner_display_name ?? link.integrity_owner"
>
{{ link.owner_display_name ?? link.integrity_owner }}
</div>
<div class="w-32 pr-4 text-sm text-gray-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,43 @@ describe('IntegrityLinkListComponent', () => {
})
})

describe('Organization column', () => {
it('should display the organization long name with a title tooltip, falling back to the short name', async () => {
const fixture = TestBed.createComponent(IntegrityLinkListComponent)

const req = httpMock.expectOne(
'http://localhost:8000/ingestion/integrity-links/?offset=0'
)
req.flush({
items: [
{
...createMockItem('1'),
organization_display_name: 'Organisation Longue'
},
createMockItem('2')
],
has_more: false,
offset: 0
})

await new Promise((resolve) => setTimeout(resolve, 10))
fixture.detectChanges()

const cells = Array.from<HTMLElement>(
fixture.nativeElement.querySelectorAll('.group > div:nth-child(2)')
)

expect(cells.map((el) => el.textContent?.trim())).toEqual([
'Organisation Longue',
'org'
])
expect(cells.map((el) => el.title)).toEqual([
'Organisation Longue',
'org'
])
})
})

describe('Load More Pagination', () => {
it('should append items when loadMore() is called', async () => {
const fixture = TestBed.createComponent(IntegrityLinkListComponent)
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Aufnahme zu konfigurieren",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
"integrityLinks.loadMore": "Load more",
"integrityLinks.noFinalTable": "Ingestion to configure",
"integrityLinks.noResults": "No results found",
"integrityLinks.organization": "Organization",
"integrityLinks.owner": "Author",
"integrityLinks.readOnly": "Read only",
"integrityLinks.recurrence": "Recurrence",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Ingesta por configurar",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@
"integrityLinks.loadMore": "Charger plus",
"integrityLinks.noFinalTable": "Brouillon",
"integrityLinks.noResults": "Aucun résultat trouvé",
"integrityLinks.organization": "Organisation",
"integrityLinks.owner": "Propriétaire",
"integrityLinks.readOnly": "Lecture seule",
"integrityLinks.recurrence": "Récurrence",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Ingestion da configurare",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Ingestie te configureren",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Ingestão a configurar",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/translations/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"integrityLinks.loadMore": "",
"integrityLinks.noFinalTable": "Príjem na konfiguráciu",
"integrityLinks.noResults": "",
"integrityLinks.organization": "",
"integrityLinks.owner": "",
"integrityLinks.readOnly": "",
"integrityLinks.recurrence": "",
Expand Down
Loading