Skip to content

feat(application): add license notification#3553

Draft
ashwinvaidya17 wants to merge 10 commits into
open-edge-platform:mainfrom
ashwinvaidya17:ashwin/feat/license_acceptance
Draft

feat(application): add license notification#3553
ashwinvaidya17 wants to merge 10 commits into
open-edge-platform:mainfrom
ashwinvaidya17:ashwin/feat/license_acceptance

Conversation

@ashwinvaidya17
Copy link
Copy Markdown
Contributor

@ashwinvaidya17 ashwinvaidya17 commented Apr 20, 2026

📝 Description

✨ Changes

Select what type of change your PR is:

  • 🚀 New feature (non-breaking change which adds functionality)
  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • 🔄 Refactor (non-breaking change which refactors the code base)
  • ⚡ Performance improvements
  • 🎨 Style changes (code style/formatting)
  • 🧪 Tests (adding/modifying tests)
  • 📚 Documentation update
  • 📦 Build system changes
  • 🚧 CI/CD configuration
  • 🔧 Chore (general maintenance)
  • 🔒 Security update
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)

✅ Checklist

Before you submit your pull request, please make sure you have completed the following steps:

  • 📚 I have made the necessary updates to the documentation (if applicable).
  • 🧪 I have written tests that support my changes and prove that my fix is effective or my feature works (if applicable).
  • 🏷️ My PR title follows conventional commit format.

For more information about code review checklists, see the Code Review Checklist.

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Comment thread application/backend/tests/unit/services/test_system_service.py Fixed
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a version-scoped license acceptance flow to Anomalib Studio (application backend + UI), persisting acceptance in the Studio DB and blocking the UI until required licenses are accepted, with license lists varying by deployment type and model.

Changes:

  • Backend: adds license acceptance persistence (license_acceptance table), /api/system/license + /api/system/license:accept endpoints, and deployment type inference exposed via SystemInfo.
  • UI: introduces a LicenseGate provider that fetches license status and prompts acceptance before continuing.
  • Updates tests/fixtures and third-party-programs.txt to include SuperSimpleNet attribution.

Reviewed changes

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

Show a summary per file
File Description
third-party-programs.txt Adds SuperSimpleNet model attribution entry.
application/ui/tests/fixtures.ts Mocks new license endpoints for Playwright component tests.
application/ui/src/setup-tests.ts Adds storage stubs and switches MSW server import to dynamic import for test setup.
application/ui/src/providers.tsx Wraps router with LicenseGate to enforce license acceptance.
application/ui/src/features/license/license-gate.component.tsx Implements UI license gating dialog + accept flow.
application/ui/src/features/license/license-gate.component.test.tsx Adds unit test validating gating/accept flow.
application/backend/tests/unit/services/test_system_service.py Adds unit tests for license status and required licenses.
application/backend/tests/unit/endpoints/test_system.py Adds endpoint tests for license status + accept endpoints.
application/backend/src/services/system_service.py Adds deployment inference, required license list, DB persistence, and extends SystemInfo.
application/backend/src/services/init.py Exports SystemService.
application/backend/src/pydantic_models/system.py Adds DeploymentType and license-related response models; extends SystemInfo.
application/backend/src/pydantic_models/init.py Exposes new system/license models via package exports.
application/backend/src/db/schema.py Adds LicenseAcceptanceDB table model.
application/backend/src/api/endpoints/system_endpoints.py Adds /license and /license:accept endpoints.
application/backend/src/alembic/versions/7a213a27d666_initial_schema.py Adds license_acceptance table to initial migration.

Comment thread application/ui/src/features/license/license-gate.component.tsx Outdated
Comment thread application/ui/src/features/license/license-gate.component.tsx Outdated
Comment thread application/backend/tests/unit/services/test_system_service.py
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
@ashwinvaidya17 ashwinvaidya17 marked this pull request as draft April 20, 2026 12:51
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings April 22, 2026 08:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a backend/API + UI “license acceptance gate” so Windows app deployments can require users to accept a license before continuing, and mirrors related changes from upstream PRs.

Changes:

  • Backend: introduce license status + acceptance persistence (DB table + service + endpoints) and expose deployment_type in SystemInfo.
  • UI: wrap the app router with a LicenseGate component and add corresponding unit tests/mocks.
  • Compliance: update third-party notices.

Reviewed changes

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

Show a summary per file
File Description
third-party-programs.txt Adds third-party notice entry for SuperSimpleNet implementation.
application/ui/tests/fixtures.ts Mocks new license endpoints for UI integration tests.
application/ui/src/setup-tests.ts Adds storage polyfills and changes MSW setup import ordering for tests.
application/ui/src/providers.tsx Wraps app routing with LicenseGate to enforce license acceptance.
application/ui/src/features/license/license-gate.component.tsx Implements license fetch/accept flow and blocking dialog.
application/ui/src/features/license/license-gate.component.test.tsx Adds unit coverage for license gate behaviors.
application/backend/tests/unit/services/test_system_service.py Adds unit tests for license status + required licenses logic.
application/backend/tests/unit/endpoints/test_system.py Adds endpoint tests for GET/POST license routes.
application/backend/src/services/system_service.py Adds deployment detection + license status/accept persistence + enriches SystemInfo.
application/backend/src/services/init.py Exports SystemService from services package.
application/backend/src/pydantic_models/system.py Adds deployment + license-related schemas and extends SystemInfo.
application/backend/src/pydantic_models/init.py Re-exports new system-related models.
application/backend/src/db/schema.py Adds LicenseAcceptanceDB table schema.
application/backend/src/api/endpoints/system_endpoints.py Adds /api/system/license and /api/system/license:accept endpoints.
application/backend/src/alembic/versions/7a213a27d666_initial_schema.py Adds license_acceptance table creation/drop to existing “initial” migration.

Comment on lines +100 to +107
acceptance = await self._get_latest_license_acceptance()
return LicenseStatus(
accepted=acceptance is not None,
accepted_version=acceptance.accepted_version if acceptance is not None else None,
app_version=settings.version,
deployment_type=deployment_type,
licenses=self.get_required_licenses(),
)
assert len(status.licenses) == 1

@pytest.mark.asyncio
async def test_get_license_status_requires_reaccept_on_version_change(self, fxt_system_service: SystemService):

status = await fxt_system_service.get_license_status()

assert status.accepted is True
Comment on lines +93 to +100
By continuing you agree to the{' '}
<Link
href={licenseStatus.licenses[0]?.url ?? '#'}
target='_blank'
rel='noreferrer'
>
{licenseStatus.licenses[0]?.name ?? 'license terms'}
</Link>
expect(screen.getByText('Studio content')).toBeInTheDocument();
});

expect(screen.queryByText('License Agreement')).not.toBeInTheDocument();
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings April 22, 2026 14:51
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds end-user license acceptance plumbing for Anomalib Studio (backend endpoints + DB persistence) and introduces a UI “gate” that can block the app behind a license agreement dialog, alongside test/fixture updates and third-party notices maintenance.

Changes:

  • Add backend license status/acceptance APIs and persist acceptance in SQLite via a new table.
  • Add a UI LicenseGate wrapper around routing plus unit/component test coverage and MSW/Playwright fixtures.
  • Update third-party-programs.txt with an additional model notice.

Reviewed changes

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

Show a summary per file
File Description
third-party-programs.txt Adds third-party notice for SuperSimpleNet implementation.
application/ui/tests/fixtures.ts Adds MSW handlers for new license endpoints in Playwright component tests.
application/ui/src/setup-tests.ts Adds storage mocks and adjusts MSW server import timing for Vitest setup.
application/ui/src/providers.tsx Wraps the app router with LicenseGate to enforce license confirmation.
application/ui/src/features/license/license-gate.component.tsx Introduces the license dialog gate powered by React Query and fetch.
application/ui/src/features/license/license-gate.component.test.tsx Adds unit tests for license dialog behavior.
application/backend/tests/unit/services/test_system_service.py Adds service-level tests for license status/info behavior.
application/backend/tests/unit/endpoints/test_system.py Adds endpoint tests for /api/system/license and /api/system/license:accept.
application/backend/src/services/system_service.py Implements desktop detection, license status, and acceptance persistence; adds is_desktop to system info.
application/backend/src/services/init.py Exports SystemService.
application/backend/src/pydantic_models/system.py Adds LicenseInfo, LicenseStatus, LicenseAcceptanceResponse and extends SystemInfo with is_desktop.
application/backend/src/pydantic_models/init.py Exposes new license-related pydantic models via package exports.
application/backend/src/db/schema.py Adds LicenseAcceptanceDB table mapping.
application/backend/src/api/endpoints/system_endpoints.py Adds license endpoints to the system router.
application/backend/src/alembic/versions/7a213a27d666_initial_schema.py Adds license_acceptance table creation/drop to Alembic migration.

Comment on lines +81 to +108
it('does not show dialog for non-desktop deployments', async () => {
vi.spyOn(global, 'fetch').mockImplementation(async (input, init) => {
const url = String(input);
const method = init?.method ?? 'GET';

if (url.endsWith('/api/system/license') && method === 'GET') {
return new Response(
JSON.stringify({
accepted: true,
app_version: '1.2.3',
is_desktop: false,
license: null,
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
}

return new Response(null, { status: 404 });
});

renderGate();

await waitFor(() => {
expect(screen.getByText('Studio content')).toBeInTheDocument();
});

expect(screen.queryByText('License Agreement')).not.toBeInTheDocument();
});
Comment on lines +120 to +125
class LicenseAcceptanceDB(Base):
__tablename__ = "license_acceptance"

id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.current_timestamp())

Comment on lines 22 to +29
def upgrade() -> None:
"""Upgrade schema - create all tables matching schema.py."""
op.create_table(
"license_acceptance",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("created_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
Comment on lines +78 to +108
async def _get_latest_license_acceptance() -> LicenseAcceptanceDB | None:
async with get_async_db_session_ctx() as session:
result = await session.execute(select(LicenseAcceptanceDB).order_by(desc(LicenseAcceptanceDB.id)).limit(1))
return result.scalar_one_or_none()

async def get_license_status(self) -> LicenseStatus:
"""Return whether the end-user has accepted the license terms.

Non-desktop deployments are auto-accepted. Desktop builds check for
an existing acceptance record in the database.

Returns:
A :class:`LicenseStatus` with acceptance state and license metadata.
"""
settings = get_settings()

if not self.is_desktop_deployment():
return LicenseStatus(
accepted=True,
app_version=settings.version,
is_desktop=False,
license=None,
)

acceptance = await self._get_latest_license_acceptance()
return LicenseStatus(
accepted=acceptance is not None,
app_version=settings.version,
is_desktop=True,
license=self.get_license_info(),
)
Comment on lines +94 to +100
if not self.is_desktop_deployment():
return LicenseStatus(
accepted=True,
app_version=settings.version,
is_desktop=False,
license=None,
)
Comment on lines +79 to +87
const shouldBlock = !licenseStatus.accepted;

return (
<>
{children}
<DialogContainer onDismiss={() => undefined}>
{shouldBlock ? (
<AlertDialog
variant='confirmation'
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.

License confirmation

3 participants