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
1 change: 0 additions & 1 deletion dist/1253.3400258b24ebe59d.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/1253.d34e9689f6d1f920.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/2043.21d51c2fe167c098.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/2043.4bdd2413d0ee95ec.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/2816.2f21c88e4cda31f4.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/2816.732a28513a2ef47e.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/2830.698a04802c74bfc5.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/2830.834124b37a055da8.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/2841.5fba958ef939fbc2.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/2841.a98cbf0354191c0c.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/3280.38be69a22a976f4f.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/3280.639c0e6febaf179a.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/3386.09d131c3805ddbb4.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/3386.a7a425ab6245602a.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/3587.279d2d3a66efb79e.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/3587.b1716308a87d9323.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/3709.066f688b3a51c69c.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/4412.a41ab5fd02c111ba.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/4440.3124ccc9eb8403e8.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/4440.49aaeec5c32002ec.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/5186.260a959a311ae078.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/5186.bf2158d6e38b0ec9.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/5632.1c1435fc3c344740.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/5632.efcc0a0f173eb8ac.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/582.4b1eda425d5188db.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/582.b3c0df8d3c3e84ac.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/6049.7a6a850f2ff06825.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/6049.8ccee7f6f6820d11.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/6755.19c1ab31c4c59ccd.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/6755.3fe9a6af80c0a3a1.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/7649.918c444314eb1310.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/7649.c7846c3274639388.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/8270.ba07a82722ac15d2.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/8270.ee8bcfc1360571ac.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/9864.6429be060cc4f61c.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/9864.6c389c5e29898e1d.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/987.9256f6c3356f5982.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/987.de33e41bbe272af4.js

This file was deleted.

29 changes: 29 additions & 0 deletions dist/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,35 @@
"noFilterResults": "No results match your filter.",
"clearFilter": "Clear filter",
"retry": "Retry",
"accessUsage": {
"lastUsed": "Last used",
"lastActive": "Last active",
"never": "Never",
"unreferenced": "Unreferenced",
"disabledButAttempted": "Inactive, but clients are still sending it",
"filter": {
"label": "Not used in",
"any": "Any",
"days30": "30 days",
"days90": "90 days",
"days180": "180 days",
"never": "Never used"
},
"tooltip": {
"lastUsed": "Last used {{at}}",
"lastUsedOn": "Last used on {{service}} (HTTP {{status}})",
"lastUsedOnService": "Last used on {{service}}",
"neverUsed": "No use recorded",
"neverUsedSince": "No use recorded since {{since}}",
"lastDenied": "Last denied {{at}}",
"disabledButAttempted": "Inactive, but clients are still sending it",
"stale": "Stale: not used in the last {{days}} days",
"staleNoWindow": "Stale: not used recently",
"lastLogin": "Last login {{at}}",
"requests30d": "Requests in the last 30 days: {{count}}",
"unreferenced": "No API key, user, or auth provider references this role"
}
},
"skeleton": {
"loading": "Loading content"
},
Expand Down
4 changes: 2 additions & 2 deletions dist/index.html

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/main.ba2d28543ada85a3.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/main.e7d393ee91a683e6.js

This file was deleted.

1 change: 1 addition & 0 deletions dist/runtime.77b1e8516107f95b.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion dist/runtime.e5fc0bbe2f048fb4.js

This file was deleted.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class DfManageAdminsTableComponent extends DfManageTableComponent<UserRow
dialog: MatDialog
) {
super(router, activatedRoute, liveAnnouncer, translateService, dialog);
this.enableAccessUsage('user', { header: 'accessUsage.lastActive' });
}
override columns = USER_COLUMNS;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@
</button>
<ng-content select="[topActions]"></ng-content>
<div class="spacer"></div>
<ng-container *ngIf="accessUsage as usage">
<mat-form-field
*ngIf="usage.available"
class="df-usage-filter"
data-testid="access-usage-filter"
appearance="outline"
subscriptSizing="dynamic">
<mat-label>{{ 'accessUsage.filter.label' | transloco }}</mat-label>
<mat-select [formControl]="usage.filter">
<mat-option
*ngFor="let option of usage.filterOptions"
[value]="option.value">
{{ option.label | transloco }}
</mat-option>
</mat-select>
</mat-form-field>
</ng-container>
<mat-form-field
*ngIf="allowFilter"
class="search-input"
Expand Down Expand Up @@ -117,10 +134,18 @@
column.columnDef !== 'active' &&
column.columnDef !== 'tokens' &&
column.columnDef !== 'spend' &&
column.columnDef !== 'role'
column.columnDef !== 'role' &&
column.columnDef !== 'lastUsed'
">
{{ column?.cell(row) }}
</ng-container>
<df-access-usage-cell
*ngIf="column.columnDef === 'lastUsed'"
[usage]="accessUsage?.get(row.id)"
[staleDays]="accessUsage?.staleDays ?? null"
[trackingStartedAt]="
accessUsage?.trackingStartedAt ?? null
"></df-access-usage-cell>
</td>
</ng-container>
<ng-container
Expand Down Expand Up @@ -246,7 +271,11 @@
<td class="mat-cell" [attr.colspan]="columns.length">
<ng-container
*ngIf="tableState !== 'loading' && tableState !== 'error'">
<ng-container *ngIf="currentFilter.value; else noFilterActive">
<ng-container
*ngIf="
currentFilter.value || accessUsage?.filterActive;
else noFilterActive
">
{{ 'noFilterResults' | transloco }}
<button
mat-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ export class DfManageAppsTableComponent extends DfManageTableComponent<VirtualKe
}

this.loadMetrics();
this.enableAccessUsage('app', {
header: 'accessUsage.lastUsed',
before: 'tokens',
});
}

override columns = [
Expand Down
63 changes: 63 additions & 0 deletions src/app/adf-apps/df-manage-apps/df-manage-apps-table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import { TranslocoHttpLoader } from 'src/transloco-loader';
import { of } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import {
DfAccessUsageService,
unavailableAccessUsage,
} from 'src/app/shared/services/df-access-usage.service';
import { AccessUsageResult } from 'src/app/shared/types/access-usage';
import { DfSnackbarService } from 'src/app/shared/services/df-snackbar.service';

describe('DfManageAppsTableComponent', () => {
let component: DfManageAppsTableComponent;
Expand Down Expand Up @@ -45,3 +51,60 @@ describe('DfManageAppsTableComponent', () => {
expect(component).toBeTruthy();
});
});

describe('DfManageAppsTableComponent access usage column', () => {
const setup = (result: AccessUsageResult) => {
TestBed.configureTestingModule({
imports: [
DfManageAppsTableComponent,
HttpClientTestingModule,
NoopAnimationsModule,
],
providers: [
provideTransloco({
config: { defaultLang: 'en', availableLangs: ['en'] },
loader: TranslocoHttpLoader,
}),
TranslocoService,
{ provide: ActivatedRoute, useValue: { data: of({}) } },
{
provide: DfAccessUsageService,
useValue: { load: jest.fn(() => of(result)) },
},
{
provide: DfSnackbarService,
useValue: { setSnackbarLastEle: jest.fn() },
},
],
});
const fixture = TestBed.createComponent(DfManageAppsTableComponent);
fixture.detectChanges();
return fixture;
};

it('keeps the existing columns when access usage is unavailable', () => {
const fixture = setup(unavailableAccessUsage());
expect(fixture.componentInstance.displayedColumns).toEqual([
'active',
'name',
'role',
'apiKey',
'tokens',
'spend',
'meter',
'actions',
]);
expect(
fixture.nativeElement.querySelector('[data-testid="access-usage-filter"]')
).toBeNull();
});

it('adds "Last used" ahead of the metering columns when available', () => {
const fixture = setup({ available: true, rows: new Map(), meta: null });
const columns = fixture.componentInstance.displayedColumns;
expect(columns.indexOf('lastUsed')).toBe(columns.indexOf('tokens') - 1);
expect(
fixture.nativeElement.querySelector('[data-testid="access-usage-filter"]')
).not.toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class DfManageRolesTableComponent extends DfManageTableComponent<RoleRow>
duplicateAction
);
}
this.enableAccessUsage('role', { header: 'accessUsage.lastUsed' });
}

filterQuery = getFilterQuery('roles');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ const EXPECTED_TOOL_NAMES = [
'create_app',
'get_app',
'list_admins',
'get_access_audit',
'call_system_api',
];

describe('SYSTEM_MCP_TOOLS catalogue', () => {
it('contains exactly the 17 tools exposed by df-system-mcp-server', () => {
expect(SYSTEM_MCP_TOOLS.length).toBe(17);
it('contains exactly the 18 tools exposed by df-system-mcp-server', () => {
expect(SYSTEM_MCP_TOOLS.length).toBe(18);
expect(SYSTEM_MCP_TOOLS.map(t => t.name)).toEqual(EXPECTED_TOOL_NAMES);
});

Expand Down
7 changes: 7 additions & 0 deletions src/app/adf-services/df-service-details/system-mcp-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ export const SYSTEM_MCP_TOOLS: ReadonlyArray<SystemMcpTool> = [
title: 'List Admins',
description: 'List the administrator accounts on this instance.',
},
// Access audit (read-only)
{
name: 'get_access_audit',
title: 'Get Access Audit',
description:
'Report last-used / never-used / stale API keys, roles and users from system/access_usage.',
},
// Generic
{
name: 'call_system_api',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class DfManageUsersTableComponent extends DfManageTableComponent<UserRow>
dialog: MatDialog
) {
super(router, activatedRoute, liveAnnouncer, translateService, dialog);
this.enableAccessUsage('user', { header: 'accessUsage.lastActive' });
}
override columns = USER_COLUMNS;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<span
class="df-usage"
data-testid="access-usage-cell"
[class.df-usage--muted]="view.muted"
[matTooltip]="tooltip"
matTooltipClass="df-usage-tooltip">
<ng-container *ngIf="view.warning">
<fa-icon
class="df-usage__warning"
data-testid="access-usage-warning"
aria-hidden="true"
[icon]="faTriangleExclamation"></fa-icon>
<span class="cdk-visually-hidden">{{
'accessUsage.disabledButAttempted' | transloco
}}</span>
</ng-container>
<span class="df-usage__label">{{
view.relative ?? ('accessUsage.never' | transloco)
}}</span>
</span>
<df-badge
*ngIf="view.unreferenced"
data-testid="access-usage-unreferenced"
variant="warning"
[dot]="false"
[label]="'accessUsage.unreferenced' | transloco"></df-badge>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Token-driven so light, dark, and phosphor all stay legible.
:host {
display: inline-flex;
align-items: center;
gap: var(--df-space-2);
white-space: nowrap;
}

.df-usage {
display: inline-flex;
align-items: center;
gap: var(--df-space-2);
}

// Never used or stale: quiet, not alarming.
.df-usage--muted {
color: var(--df-text-faint);
}

// Inactive credential still being sent.
.df-usage__warning {
color: var(--df-warning);
}
Loading
Loading