From b8eaa483ee0aafc916eefb03c0a9b1d27daaad68 Mon Sep 17 00:00:00 2001 From: John Harrington <84741727+harr1424@users.noreply.github.com> Date: Fri, 10 Oct 2025 07:43:36 -0700 Subject: [PATCH] ensure files moved to @bitwarden/logging appear as moves in history --- .../credential-generator-history.component.ts | 10 +- .../default-send-access-service.spec.ts | 15 +- .../default-send-access-service.ts | 9 +- .../send-access/try-send-access.guard.spec.ts | 39 ++- .../send/send-access/try-send-access.guard.ts | 8 +- libs/angular/src/services/injection-tokens.ts | 2 + .../tools/extension/extension.service.spec.ts | 3 +- .../src/tools/extension/extension.service.ts | 3 +- .../user-state-subject-dependency-provider.ts | 3 +- .../tools/state/user-state-subject.spec.ts | 3 +- .../src/tools/state/user-state-subject.ts | 3 +- .../src}/default-semantic-logger.spec.ts | 0 .../src}/default-semantic-logger.ts | 0 .../log => logging/src}/disabled-logger.ts | 0 .../src/tools/log => logging/src}/factory.ts | 0 libs/logging/src/index.ts | 47 ++++ libs/logging/src/logging.spec.ts | 95 ++++++- .../src}/semantic-logger.abstraction.ts | 0 ...tial-generator-history-dialog.component.ts | 10 +- .../credential-generator-history.component.ts | 10 +- .../src/credential-generator.component.ts | 10 +- libs/tools/generator/components/src/index.ts | 3 +- .../src/passphrase-settings.component.ts | 10 +- .../src/password-generator.component.ts | 10 +- .../src/username-generator.component.ts | 10 +- .../available-algorithms-constraint.ts | 2 +- .../generator-metadata-provider.spec.ts | 245 ++++++++++++------ .../generator-profile-provider.spec.ts | 2 +- .../providers/generator-profile-provider.ts | 2 +- ...fault-credential-generator.service.spec.ts | 29 ++- .../default-credential-generator.service.ts | 14 +- 31 files changed, 405 insertions(+), 192 deletions(-) rename libs/{common/src/tools/log => logging/src}/default-semantic-logger.spec.ts (100%) rename libs/{common/src/tools/log => logging/src}/default-semantic-logger.ts (100%) rename libs/{common/src/tools/log => logging/src}/disabled-logger.ts (100%) rename libs/{common/src/tools/log => logging/src}/factory.ts (100%) rename libs/{common/src/tools/log => logging/src}/semantic-logger.abstraction.ts (100%) diff --git a/apps/browser/src/tools/popup/generator/credential-generator-history.component.ts b/apps/browser/src/tools/popup/generator/credential-generator-history.component.ts index 441e5d6e4c6..f0a0a861ac5 100644 --- a/apps/browser/src/tools/popup/generator/credential-generator-history.component.ts +++ b/apps/browser/src/tools/popup/generator/credential-generator-history.component.ts @@ -7,11 +7,6 @@ import { ReplaySubject, Subject, firstValueFrom, map, switchMap, takeUntil } fro import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ButtonModule, DialogService } from "@bitwarden/components"; import { @@ -19,6 +14,11 @@ import { EmptyCredentialHistoryComponent, } from "@bitwarden/generator-components"; import { GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component"; diff --git a/apps/web/src/app/tools/send/send-access/default-send-access-service.spec.ts b/apps/web/src/app/tools/send/send-access/default-send-access-service.spec.ts index cd07d3684fb..365d47616cd 100644 --- a/apps/web/src/app/tools/send/send-access/default-send-access-service.spec.ts +++ b/apps/web/src/app/tools/send/send-access/default-send-access-service.spec.ts @@ -3,14 +3,13 @@ import { Router, UrlTree } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, NEVER } from "rxjs"; +import { LOG_PROVIDER } from "@bitwarden/angular/services/injection-tokens"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { StateProvider } from "@bitwarden/common/platform/state"; import { mockAccountServiceWith, FakeStateProvider } from "@bitwarden/common/spec"; -import { SemanticLogger } from "@bitwarden/common/tools/log"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { UserId } from "@bitwarden/common/types/guid"; -import { SYSTEM_SERVICE_PROVIDER } from "@bitwarden/generator-components"; +import { LogProvider, SemanticLogger } from "@bitwarden/logging"; import { DefaultSendAccessService } from "./default-send-access-service"; import { SEND_RESPONSE_KEY, SEND_CONTEXT_KEY } from "./send-access-memory"; @@ -21,7 +20,7 @@ describe("DefaultSendAccessService", () => { let sendApiService: MockProxy; let router: MockProxy; let logger: MockProxy; - let systemServiceProvider: MockProxy; + let logProvider: LogProvider; beforeEach(() => { const accountService = mockAccountServiceWith("user-id" as UserId); @@ -29,9 +28,7 @@ describe("DefaultSendAccessService", () => { sendApiService = mock(); router = mock(); logger = mock(); - systemServiceProvider = mock(); - - systemServiceProvider.log.mockReturnValue(logger); + logProvider = jest.fn().mockReturnValue(logger) as any; TestBed.configureTestingModule({ providers: [ @@ -39,7 +36,7 @@ describe("DefaultSendAccessService", () => { { provide: StateProvider, useValue: stateProvider }, { provide: SendApiService, useValue: sendApiService }, { provide: Router, useValue: router }, - { provide: SYSTEM_SERVICE_PROVIDER, useValue: systemServiceProvider }, + { provide: LOG_PROVIDER, useValue: logProvider }, ], }); @@ -48,7 +45,7 @@ describe("DefaultSendAccessService", () => { describe("constructor", () => { it("creates logger with type 'SendAccessAuthenticationService' when initialized", () => { - expect(systemServiceProvider.log).toHaveBeenCalledWith({ + expect(logProvider).toHaveBeenCalledWith({ type: "SendAccessAuthenticationService", }); }); diff --git a/apps/web/src/app/tools/send/send-access/default-send-access-service.ts b/apps/web/src/app/tools/send/send-access/default-send-access-service.ts index 732303ce25a..0c5a8722e24 100644 --- a/apps/web/src/app/tools/send/send-access/default-send-access-service.ts +++ b/apps/web/src/app/tools/send/send-access/default-send-access-service.ts @@ -2,13 +2,12 @@ import { Injectable, Inject } from "@angular/core"; import { Router, UrlTree } from "@angular/router"; import { map, of, from, catchError, timeout } from "rxjs"; +import { LOG_PROVIDER } from "@bitwarden/angular/services/injection-tokens"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { StateProvider } from "@bitwarden/common/platform/state"; -import { SemanticLogger } from "@bitwarden/common/tools/log"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SYSTEM_SERVICE_PROVIDER } from "@bitwarden/generator-components"; +import { LogProvider, SemanticLogger } from "@bitwarden/logging"; import { SEND_RESPONSE_KEY, SEND_CONTEXT_KEY } from "./send-access-memory"; import { SendAccessService } from "./send-access-service.abstraction"; @@ -24,9 +23,9 @@ export class DefaultSendAccessService implements SendAccessService { private readonly state: StateProvider, private readonly api: SendApiService, private readonly router: Router, - @Inject(SYSTEM_SERVICE_PROVIDER) system: SystemServiceProvider, + @Inject(LOG_PROVIDER) log: LogProvider, ) { - this.logger = system.log({ type: "SendAccessAuthenticationService" }); + this.logger = log({ type: "SendAccessAuthenticationService" }); } redirect$(sendId: string) { diff --git a/apps/web/src/app/tools/send/send-access/try-send-access.guard.spec.ts b/apps/web/src/app/tools/send/send-access/try-send-access.guard.spec.ts index 267de83db9f..824898ce6d5 100644 --- a/apps/web/src/app/tools/send/send-access/try-send-access.guard.spec.ts +++ b/apps/web/src/app/tools/send/send-access/try-send-access.guard.spec.ts @@ -2,9 +2,8 @@ import { TestBed } from "@angular/core/testing"; import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from "@angular/router"; import { firstValueFrom, Observable, of } from "rxjs"; -import { SemanticLogger } from "@bitwarden/common/tools/log"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; -import { SYSTEM_SERVICE_PROVIDER } from "@bitwarden/generator-components"; +import { LOG_PROVIDER } from "@bitwarden/angular/services/injection-tokens"; +import { LogProvider, SemanticLogger } from "@bitwarden/logging"; import { SendAccessService } from "./send-access-service.abstraction"; import { trySendAccess } from "./try-send-access.guard"; @@ -22,10 +21,8 @@ function createMockLogger(): SemanticLogger { } as any as SemanticLogger; } -function createMockSystemServiceProvider(): SystemServiceProvider { - return { - log: jest.fn().mockReturnValue(createMockLogger()), - } as any as SystemServiceProvider; +function createMockLogProvider(): LogProvider { + return jest.fn().mockReturnValue(createMockLogger()) as any; } function createMockSendAccessService() { @@ -38,18 +35,18 @@ function createMockSendAccessService() { describe("trySendAccess", () => { let mockSendAccessService: ReturnType; - let mockSystemServiceProvider: SystemServiceProvider; + let mockLogProvider: LogProvider; let mockRouterState: RouterStateSnapshot; beforeEach(() => { mockSendAccessService = createMockSendAccessService(); - mockSystemServiceProvider = createMockSystemServiceProvider(); + mockLogProvider = createMockLogProvider(); mockRouterState = {} as RouterStateSnapshot; TestBed.configureTestingModule({ providers: [ { provide: SendAccessService, useValue: mockSendAccessService }, - { provide: SYSTEM_SERVICE_PROVIDER, useValue: mockSystemServiceProvider }, + { provide: LOG_PROVIDER, useValue: mockLogProvider }, ], }); }); @@ -97,7 +94,7 @@ describe("trySendAccess", () => { await expect(firstValueFrom(guardResult!)).resolves.toEqual(expectedUrlTree); // Logger methods should not be called for warnings or panics - const mockLogger = (mockSystemServiceProvider.log as jest.Mock).mock.results[0].value; + const mockLogger = (mockLogProvider as jest.Mock).mock.results[0].value; expect(mockLogger.warn).not.toHaveBeenCalled(); expect(mockLogger.panic).not.toHaveBeenCalled(); }); @@ -116,7 +113,7 @@ describe("trySendAccess", () => { sendIdValue === undefined ? { key } : { sendId: sendIdValue, key }, ); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => @@ -125,7 +122,7 @@ describe("trySendAccess", () => { await firstValueFrom(result$); }).rejects.toThrow("Logger panic called"); - expect(mockSystemServiceProvider.log).toHaveBeenCalledWith({ + expect(mockLogProvider).toHaveBeenCalledWith({ function: "trySendAccess", }); expect(mockLogger.warn).toHaveBeenCalledWith( @@ -142,7 +139,7 @@ describe("trySendAccess", () => { const key = "valid-key"; const mockRoute = createMockRoute({ sendId: value, key }); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => @@ -151,7 +148,7 @@ describe("trySendAccess", () => { await firstValueFrom(result$); }).rejects.toThrow("Logger panic called"); - expect(mockSystemServiceProvider.log).toHaveBeenCalledWith({ function: "trySendAccess" }); + expect(mockLogProvider).toHaveBeenCalledWith({ function: "trySendAccess" }); expect(mockLogger.panic).toHaveBeenCalledWith( { expected: "string", actual: type }, "sendId has invalid type", @@ -167,7 +164,7 @@ describe("trySendAccess", () => { invalidSendId === undefined ? { key } : { sendId: invalidSendId, key }, ); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => @@ -189,7 +186,7 @@ describe("trySendAccess", () => { keyValue === undefined ? { sendId } : { sendId, key: keyValue }, ); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => @@ -198,7 +195,7 @@ describe("trySendAccess", () => { await firstValueFrom(result$); }).rejects.toThrow("Logger panic called"); - expect(mockSystemServiceProvider.log).toHaveBeenCalledWith({ function: "trySendAccess" }); + expect(mockLogProvider).toHaveBeenCalledWith({ function: "trySendAccess" }); expect(mockLogger.panic).toHaveBeenCalledWith("key missing from the route parameters"); }); @@ -210,7 +207,7 @@ describe("trySendAccess", () => { const sendId = "valid-send-id"; const mockRoute = createMockRoute({ sendId, key: value }); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => @@ -219,7 +216,7 @@ describe("trySendAccess", () => { await firstValueFrom(result$); }).rejects.toThrow("Logger panic called"); - expect(mockSystemServiceProvider.log).toHaveBeenCalledWith({ function: "trySendAccess" }); + expect(mockLogProvider).toHaveBeenCalledWith({ function: "trySendAccess" }); expect(mockLogger.panic).toHaveBeenCalledWith( { expected: "string", actual: type }, "key has invalid type", @@ -235,7 +232,7 @@ describe("trySendAccess", () => { invalidKey === undefined ? { sendId } : { sendId, key: invalidKey }, ); const mockLogger = createMockLogger(); - (mockSystemServiceProvider.log as jest.Mock).mockReturnValue(mockLogger); + (mockLogProvider as jest.Mock).mockReturnValue(mockLogger); await expect(async () => { const result$ = TestBed.runInInjectionContext(() => diff --git a/apps/web/src/app/tools/send/send-access/try-send-access.guard.ts b/apps/web/src/app/tools/send/send-access/try-send-access.guard.ts index 51941bf8e74..b75f55e5e55 100644 --- a/apps/web/src/app/tools/send/send-access/try-send-access.guard.ts +++ b/apps/web/src/app/tools/send/send-access/try-send-access.guard.ts @@ -2,8 +2,8 @@ import { inject } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from "@angular/router"; import { from, ignoreElements, concat } from "rxjs"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; -import { SYSTEM_SERVICE_PROVIDER } from "@bitwarden/generator-components"; +import { LOG_PROVIDER } from "@bitwarden/angular/services/injection-tokens"; +import { LogProvider } from "@bitwarden/logging"; import { SendAccessService } from "./send-access-service.abstraction"; @@ -12,8 +12,8 @@ export const trySendAccess: CanActivateFn = ( _state: RouterStateSnapshot, ) => { const sendAccess = inject(SendAccessService); - const system = inject(SYSTEM_SERVICE_PROVIDER); - const logger = system.log({ function: "trySendAccess" }); + const log = inject(LOG_PROVIDER); + const logger = log({ function: "trySendAccess" }); const { sendId, key } = route.params; if (!sendId) { diff --git a/libs/angular/src/services/injection-tokens.ts b/libs/angular/src/services/injection-tokens.ts index 6bf3ab77252..0c259bd6783 100644 --- a/libs/angular/src/services/injection-tokens.ts +++ b/libs/angular/src/services/injection-tokens.ts @@ -15,10 +15,12 @@ import { import { Theme } from "@bitwarden/common/platform/enums"; import { Message } from "@bitwarden/common/platform/messaging"; import { HttpOperations } from "@bitwarden/common/services/api.service"; +import { LogProvider } from "@bitwarden/logging"; import { SafeInjectionToken } from "@bitwarden/ui-common"; // Re-export the SafeInjectionToken from ui-common export { SafeInjectionToken } from "@bitwarden/ui-common"; +export const LOG_PROVIDER = new SafeInjectionToken("LogProvider"); export const WINDOW = new SafeInjectionToken("WINDOW"); export const DOCUMENT = new SafeInjectionToken("DOCUMENT"); export const OBSERVABLE_MEMORY_STORAGE = new SafeInjectionToken< diff --git a/libs/common/src/tools/extension/extension.service.spec.ts b/libs/common/src/tools/extension/extension.service.spec.ts index 9959488feca..e17e4148cad 100644 --- a/libs/common/src/tools/extension/extension.service.spec.ts +++ b/libs/common/src/tools/extension/extension.service.spec.ts @@ -1,13 +1,14 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom } from "rxjs"; +import { disabledSemanticLoggerProvider } from "@bitwarden/logging"; + import { FakeAccountService, FakeStateProvider, awaitAsync } from "../../../spec"; import { Account } from "../../auth/abstractions/account.service"; import { EXTENSION_DISK, UserKeyDefinition } from "../../platform/state"; import { UserId } from "../../types/guid"; import { LegacyEncryptorProvider } from "../cryptography/legacy-encryptor-provider"; import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; -import { disabledSemanticLoggerProvider } from "../log"; import { UserStateSubjectDependencyProvider } from "../state/user-state-subject-dependency-provider"; import { Site } from "./data"; diff --git a/libs/common/src/tools/extension/extension.service.ts b/libs/common/src/tools/extension/extension.service.ts index c4e6887b821..4532fe9943a 100644 --- a/libs/common/src/tools/extension/extension.service.ts +++ b/libs/common/src/tools/extension/extension.service.ts @@ -1,8 +1,9 @@ import { shareReplay } from "rxjs"; +import { SemanticLogger } from "@bitwarden/logging"; + import { Account } from "../../auth/abstractions/account.service"; import { BoundDependency } from "../dependencies"; -import { SemanticLogger } from "../log"; import { UserStateSubject } from "../state/user-state-subject"; import { UserStateSubjectDependencyProvider } from "../state/user-state-subject-dependency-provider"; diff --git a/libs/common/src/tools/state/user-state-subject-dependency-provider.ts b/libs/common/src/tools/state/user-state-subject-dependency-provider.ts index 89b6d7e7270..7a85ab2ba55 100644 --- a/libs/common/src/tools/state/user-state-subject-dependency-provider.ts +++ b/libs/common/src/tools/state/user-state-subject-dependency-provider.ts @@ -1,8 +1,9 @@ import { Jsonify } from "type-fest"; +import { SemanticLogger } from "@bitwarden/logging"; + import { StateProvider } from "../../platform/state"; import { LegacyEncryptorProvider } from "../cryptography/legacy-encryptor-provider"; -import { SemanticLogger } from "../log"; /** Aggregates user state subject dependencies */ export abstract class UserStateSubjectDependencyProvider { diff --git a/libs/common/src/tools/state/user-state-subject.spec.ts b/libs/common/src/tools/state/user-state-subject.spec.ts index a6d452d37fd..bdf2c1f91ec 100644 --- a/libs/common/src/tools/state/user-state-subject.spec.ts +++ b/libs/common/src/tools/state/user-state-subject.spec.ts @@ -2,6 +2,8 @@ // @ts-strict-ignore import { BehaviorSubject, of, Subject } from "rxjs"; +import { disabledSemanticLoggerProvider } from "@bitwarden/logging"; + import { awaitAsync, FakeAccountService, @@ -13,7 +15,6 @@ import { GENERATOR_DISK, UserKeyDefinition } from "../../platform/state"; import { UserId } from "../../types/guid"; import { LegacyEncryptorProvider } from "../cryptography/legacy-encryptor-provider"; import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; -import { disabledSemanticLoggerProvider } from "../log"; import { PrivateClassifier } from "../private-classifier"; import { StateConstraints } from "../types"; diff --git a/libs/common/src/tools/state/user-state-subject.ts b/libs/common/src/tools/state/user-state-subject.ts index e6b66d8f699..0bf813b4a62 100644 --- a/libs/common/src/tools/state/user-state-subject.ts +++ b/libs/common/src/tools/state/user-state-subject.ts @@ -29,11 +29,12 @@ import { switchMap, } from "rxjs"; +import { SemanticLogger } from "@bitwarden/logging"; + import { Account } from "../../auth/abstractions/account.service"; import { EncString } from "../../key-management/crypto/models/enc-string"; import { SingleUserState, UserKeyDefinition } from "../../platform/state"; import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; -import { SemanticLogger } from "../log"; import { anyComplete, pin, ready, withLatestReady } from "../rx"; import { Constraints, SubjectConstraints, WithConstraints } from "../types"; diff --git a/libs/common/src/tools/log/default-semantic-logger.spec.ts b/libs/logging/src/default-semantic-logger.spec.ts similarity index 100% rename from libs/common/src/tools/log/default-semantic-logger.spec.ts rename to libs/logging/src/default-semantic-logger.spec.ts diff --git a/libs/common/src/tools/log/default-semantic-logger.ts b/libs/logging/src/default-semantic-logger.ts similarity index 100% rename from libs/common/src/tools/log/default-semantic-logger.ts rename to libs/logging/src/default-semantic-logger.ts diff --git a/libs/common/src/tools/log/disabled-logger.ts b/libs/logging/src/disabled-logger.ts similarity index 100% rename from libs/common/src/tools/log/disabled-logger.ts rename to libs/logging/src/disabled-logger.ts diff --git a/libs/common/src/tools/log/factory.ts b/libs/logging/src/factory.ts similarity index 100% rename from libs/common/src/tools/log/factory.ts rename to libs/logging/src/factory.ts diff --git a/libs/logging/src/index.ts b/libs/logging/src/index.ts index 8ce4b62cd3f..65e8c03605f 100644 --- a/libs/logging/src/index.ts +++ b/libs/logging/src/index.ts @@ -1,3 +1,50 @@ +import { Jsonify } from "type-fest"; + +import { SemanticLogger } from "./semantic-logger.abstraction"; + export { LogService } from "./log.service"; export { LogLevel } from "./log-level"; export { ConsoleLogService } from "./console-log.service"; +export { SemanticLogger } from "./semantic-logger.abstraction"; +export { DISABLED_LOGGER } from "./disabled-logger"; +export { + disabledSemanticLoggerProvider, + consoleSemanticLoggerProvider, + enableLogForTypes, + ifEnabledSemanticLoggerProvider, +} from "./factory"; + +/** + * Creates a semantic logger with a fixed context that is included in all log messages. + * + * @param context - Contextual metadata that will be included in every log entry + * emitted by the returned logger. This is used to identify the source or scope + * of log messages (e.g., `{ type: "ImportService" }` or `{ accountId: "123" }`). + * + * @returns A SemanticLogger instance that includes the provided context in all log output. + * + * @remarks + * By convention, avoid using the following field names in the context object, as they + * may conflict with fields added by the semantic logging implementation: + * - `message` - The log message text + * - `level` - The log level (debug, info, warn, error, panic) + * - `provider` - The logging provider identifier + * - `content` - Additional data passed to individual log calls + * + * Note: These field names are not enforced at compile-time or runtime, but using them + * may result in unexpected behavior or field name collisions in log output. + * + * @example + * ```typescript + * // Create a logger for a service + * const log = logProvider({ type: "ImportService" }); + * + * // All logs from this logger will include { type: "ImportService" } + * log.debug("Starting import"); + * // Output: { type: "ImportService", level: "debug", message: "Starting import" } + * + * log.info({ itemCount: 42 }, "Import complete"); + * // Output: { type: "ImportService", level: "info", content: { itemCount: 42 }, message: "Import complete" } + * ``` + */ +export type LogProvider = (context: Jsonify) => SemanticLogger; diff --git a/libs/logging/src/logging.spec.ts b/libs/logging/src/logging.spec.ts index 04a057a156f..168d13a2369 100644 --- a/libs/logging/src/logging.spec.ts +++ b/libs/logging/src/logging.spec.ts @@ -1,8 +1,93 @@ -import * as lib from "./index"; +import { mock } from "jest-mock-extended"; -describe("logging", () => { - // This test will fail until something is exported from index.ts - it("should work", () => { - expect(lib).toBeDefined(); +import * as lib from "./index"; +import { SemanticLogger } from "./index"; + +describe("logging module", () => { + describe("public API", () => { + it("should export LogService", () => { + expect(lib.LogService).toBeDefined(); + }); + + it("should export LogLevel", () => { + expect(lib.LogLevel).toBeDefined(); + }); + + it("should export ConsoleLogService", () => { + expect(lib.ConsoleLogService).toBeDefined(); + }); + + it("should export DISABLED_LOGGER", () => { + expect(lib.DISABLED_LOGGER).toBeDefined(); + }); + + it("should export disabledSemanticLoggerProvider", () => { + expect(lib.disabledSemanticLoggerProvider).toBeDefined(); + }); + + it("should export consoleSemanticLoggerProvider", () => { + expect(lib.consoleSemanticLoggerProvider).toBeDefined(); + }); + + it("should export enableLogForTypes", () => { + expect(lib.enableLogForTypes).toBeDefined(); + }); + + it("should export ifEnabledSemanticLoggerProvider", () => { + expect(lib.ifEnabledSemanticLoggerProvider).toBeDefined(); + }); + }); + + describe("SemanticLogger", () => { + let logger: SemanticLogger; + + beforeEach(() => { + logger = mock(); + }); + + describe("logging methods", () => { + it("should accept a message string", () => { + logger.debug("debug message"); + logger.info("info message"); + logger.warn("warn message"); + logger.error("error message"); + + expect(logger.debug).toHaveBeenCalledWith("debug message"); + expect(logger.info).toHaveBeenCalledWith("info message"); + expect(logger.warn).toHaveBeenCalledWith("warn message"); + expect(logger.error).toHaveBeenCalledWith("error message"); + }); + + it("should accept content object and optional message", () => { + logger.debug({ step: 1 }, "processing step"); + logger.info({ count: 42 }, "items processed"); + logger.warn({ threshold: 100 }, "approaching limit"); + logger.error({ code: 500 }, "server error"); + + expect(logger.debug).toHaveBeenCalledWith({ step: 1 }, "processing step"); + expect(logger.info).toHaveBeenCalledWith({ count: 42 }, "items processed"); + expect(logger.warn).toHaveBeenCalledWith({ threshold: 100 }, "approaching limit"); + expect(logger.error).toHaveBeenCalledWith({ code: 500 }, "server error"); + }); + }); + + describe("panic", () => { + beforeEach(() => { + logger.panic = jest.fn((content: any, msg?: string) => { + const errorMsg = msg || (typeof content === "string" ? content : "panic"); + throw new Error(errorMsg); + }) as any; + }); + + it("should throw when called with a message", () => { + expect(() => logger.panic("critical error")).toThrow("critical error"); + }); + + it("should throw when called with content and message", () => { + expect(() => logger.panic({ reason: "invalid state" }, "system panic")).toThrow( + "system panic", + ); + }); + }); }); }); diff --git a/libs/common/src/tools/log/semantic-logger.abstraction.ts b/libs/logging/src/semantic-logger.abstraction.ts similarity index 100% rename from libs/common/src/tools/log/semantic-logger.abstraction.ts rename to libs/logging/src/semantic-logger.abstraction.ts diff --git a/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts b/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts index 9ec0e636f9a..36cdab22d3e 100644 --- a/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts +++ b/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts @@ -15,14 +15,14 @@ import { import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; import { GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { CredentialGeneratorHistoryComponent as CredentialGeneratorHistoryToolsComponent } from "./credential-generator-history.component"; import { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; diff --git a/libs/tools/generator/components/src/credential-generator-history.component.ts b/libs/tools/generator/components/src/credential-generator-history.component.ts index 3965b2be83e..82fcdc1cf8c 100644 --- a/libs/tools/generator/components/src/credential-generator-history.component.ts +++ b/libs/tools/generator/components/src/credential-generator-history.component.ts @@ -8,11 +8,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ColorPasswordModule, @@ -22,6 +17,11 @@ import { } from "@bitwarden/components"; import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core"; import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { GeneratorModule } from "./generator.module"; import { translate } from "./util"; diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index 78b803392df..4b180472a53 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -31,11 +31,6 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { VendorId } from "@bitwarden/common/tools/extension"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ToastService, Option } from "@bitwarden/components"; import { @@ -55,6 +50,11 @@ import { Type, } from "@bitwarden/generator-core"; import { GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { translate } from "./util"; diff --git a/libs/tools/generator/components/src/index.ts b/libs/tools/generator/components/src/index.ts index 4ec32032de0..00410fde6fe 100644 --- a/libs/tools/generator/components/src/index.ts +++ b/libs/tools/generator/components/src/index.ts @@ -2,4 +2,5 @@ export { CredentialGeneratorHistoryComponent } from "./credential-generator-hist export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component"; export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; export { GeneratorModule } from "./generator.module"; -export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module"; +export { GeneratorServicesModule } from "./generator-services.module"; +export { LOG_PROVIDER } from "@bitwarden/angular/services/injection-tokens"; diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts index b3525251392..7bcda9c1c50 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.ts +++ b/libs/tools/generator/components/src/passphrase-settings.component.ts @@ -15,16 +15,16 @@ import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } fro import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { CredentialGeneratorService, PassphraseGenerationOptions, BuiltIn, } from "@bitwarden/generator-core"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; const Controls = Object.freeze({ numWords: "numWords", diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index b293aeb7e2d..57921f67138 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -27,11 +27,6 @@ import { import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ToastService, Option } from "@bitwarden/components"; import { @@ -49,6 +44,11 @@ import { Profile, } from "@bitwarden/generator-core"; import { GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { toAlgorithmInfo, translate } from "./util"; diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index 6227bcd3f7c..deba06020dc 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -32,11 +32,6 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { VendorId } from "@bitwarden/common/tools/extension"; -import { - SemanticLogger, - disabledSemanticLoggerProvider, - ifEnabledSemanticLoggerProvider, -} from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; import { ToastService, Option } from "@bitwarden/components"; import { @@ -55,6 +50,11 @@ import { Algorithm, } from "@bitwarden/generator-core"; import { GeneratorHistoryService } from "@bitwarden/generator-history"; +import { + disabledSemanticLoggerProvider, + ifEnabledSemanticLoggerProvider, + SemanticLogger, +} from "@bitwarden/logging"; import { toAlgorithmInfo, translate } from "./util"; diff --git a/libs/tools/generator/core/src/policies/available-algorithms-constraint.ts b/libs/tools/generator/core/src/policies/available-algorithms-constraint.ts index 1824581664b..a05749e70f3 100644 --- a/libs/tools/generator/core/src/policies/available-algorithms-constraint.ts +++ b/libs/tools/generator/core/src/policies/available-algorithms-constraint.ts @@ -1,6 +1,6 @@ -import { SemanticLogger } from "@bitwarden/common/tools/log"; import { UserStateSubjectDependencyProvider } from "@bitwarden/common/tools/state/user-state-subject-dependency-provider"; import { Constraints, StateConstraints } from "@bitwarden/common/tools/types"; +import { SemanticLogger } from "@bitwarden/logging"; import { CredentialAlgorithm, CredentialType } from "../metadata"; import { CredentialPreference } from "../types"; diff --git a/libs/tools/generator/core/src/providers/generator-metadata-provider.spec.ts b/libs/tools/generator/core/src/providers/generator-metadata-provider.spec.ts index 376b46cd6e8..004cfcc1ec6 100644 --- a/libs/tools/generator/core/src/providers/generator-metadata-provider.spec.ts +++ b/libs/tools/generator/core/src/providers/generator-metadata-provider.spec.ts @@ -5,7 +5,6 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { LegacyEncryptorProvider } from "@bitwarden/common/tools/cryptography/legacy-encryptor-provider"; import { UserEncryptor } from "@bitwarden/common/tools/cryptography/user-encryptor.abstraction"; import { @@ -17,13 +16,11 @@ import { } from "@bitwarden/common/tools/extension"; import { ExtensionService } from "@bitwarden/common/tools/extension/extension.service"; import { Bitwarden } from "@bitwarden/common/tools/extension/vendor/bitwarden"; -import { disabledSemanticLoggerProvider } from "@bitwarden/common/tools/log"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; import { UserStateSubjectDependencyProvider } from "@bitwarden/common/tools/state/user-state-subject-dependency-provider"; import { deepFreeze } from "@bitwarden/common/tools/util"; import { UserId } from "@bitwarden/common/types/guid"; -import { BitwardenClient } from "@bitwarden/sdk-internal"; +import { disabledSemanticLoggerProvider } from "@bitwarden/logging"; import { FakeAccountService, FakeStateProvider } from "../../../../../common/spec"; import { Algorithm, AlgorithmsByType, CredentialAlgorithm, Type, Types } from "../metadata"; @@ -91,27 +88,6 @@ const SomePolicyService = mock(); const SomeExtensionService = mock(); -const SomeConfigService = mock; - -const SomeSdkService = mock; - -const ApplicationProvider = { - /** Policy configured by the administrative console */ - policy: SomePolicyService, - - /** Client extension metadata and profile access */ - extension: SomeExtensionService, - - /** Event monitoring and diagnostic interfaces */ - log: disabledSemanticLoggerProvider, - - /** Feature flag retrieval */ - configService: SomeConfigService, - - /** SDK access for password generation */ - sdk: SomeSdkService, -} as unknown as SystemServiceProvider; - describe("GeneratorMetadataProvider", () => { beforeEach(() => { jest.resetAllMocks(); @@ -121,17 +97,26 @@ describe("GeneratorMetadataProvider", () => { describe("constructor", () => { it("throws when the forwarder site isn't defined by the extension service", () => { SomeExtensionService.site.mockReturnValue(undefined); - expect(() => new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [])).toThrow( - "forwarder extension site not found", - ); + expect( + () => + new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ), + ).toThrow("forwarder extension site not found"); }); }); describe("metadata", () => { it("returns algorithm metadata", async () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [ - password, - ]); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [password], + ); const metadata = provider.metadata(password.id); @@ -145,13 +130,15 @@ describe("GeneratorMetadataProvider", () => { host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" }, requestedFields: [], }; - const application = { - ...ApplicationProvider, - extension: mock({ - site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), - }), - }; - const provider = new GeneratorMetadataProvider(SystemProvider, application, []); + const extension = mock({ + site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), + }); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + extension, + [], + ); const metadata = provider.metadata({ forwarder: Bitwarden.id }); @@ -159,13 +146,23 @@ describe("GeneratorMetadataProvider", () => { }); it("panics when metadata not found", async () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); expect(() => provider.metadata("not found" as any)).toThrow("metadata not found"); }); it("panics when an extension not found", async () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); expect(() => provider.metadata({ forwarder: "not found" as any })).toThrow( "extension not found", @@ -175,7 +172,12 @@ describe("GeneratorMetadataProvider", () => { describe("types", () => { it("returns the credential types", async () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = provider.types(); @@ -185,7 +187,12 @@ describe("GeneratorMetadataProvider", () => { describe("algorithms", () => { it("returns the password category's algorithms", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = provider.algorithms({ type: Type.password }); @@ -193,7 +200,12 @@ describe("GeneratorMetadataProvider", () => { }); it("returns the username category's algorithms", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = provider.algorithms({ type: Type.username }); @@ -201,7 +213,12 @@ describe("GeneratorMetadataProvider", () => { }); it("returns the email category's algorithms", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = provider.algorithms({ type: Type.email }); @@ -215,13 +232,15 @@ describe("GeneratorMetadataProvider", () => { host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" }, requestedFields: [], }; - const application = { - ...ApplicationProvider, - extension: mock({ - site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), - }), - }; - const provider = new GeneratorMetadataProvider(SystemProvider, application, []); + const extension = mock({ + site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), + }); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + extension, + [], + ); const result = provider.algorithms({ type: Type.email }); @@ -235,7 +254,12 @@ describe("GeneratorMetadataProvider", () => { [Algorithm.plusAddress], [Algorithm.username], ])("returns explicit algorithms (=%p)", (algorithm) => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = provider.algorithms({ algorithm }); @@ -249,13 +273,15 @@ describe("GeneratorMetadataProvider", () => { host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" }, requestedFields: [], }; - const application = { - ...ApplicationProvider, - extension: mock({ - site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), - }), - }; - const provider = new GeneratorMetadataProvider(SystemProvider, application, []); + const extension = mock({ + site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), + }); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + extension, + [], + ); const result = provider.algorithms({ algorithm: { forwarder: Bitwarden.id } }); @@ -263,7 +289,12 @@ describe("GeneratorMetadataProvider", () => { }); it("returns an empty array when the algorithm is invalid", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); // `any` cast required because this test subverts the type system const result = provider.algorithms({ algorithm: "an invalid algorithm" as any }); @@ -278,13 +309,15 @@ describe("GeneratorMetadataProvider", () => { host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" }, requestedFields: [], }; - const application = { - ...ApplicationProvider, - extension: mock({ - site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), - }), - }; - const provider = new GeneratorMetadataProvider(SystemProvider, application, []); + const extension = mock({ + site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])), + }); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + extension, + [], + ); // `any` cast required because this test subverts the type system const result = provider.algorithms({ @@ -295,7 +328,12 @@ describe("GeneratorMetadataProvider", () => { }); it("panics when neither an algorithm nor a category is specified", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); // `any` cast required because this test subverts the type system expect(() => provider.algorithms({} as any)).toThrow("algorithm or type required"); @@ -309,9 +347,12 @@ describe("GeneratorMetadataProvider", () => { [Algorithm.password, password], ])("gets a specific algorithm", async (algorithm, metadata) => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [ - metadata, - ]); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [metadata], + ); const result = new ReplaySubject(1); provider.algorithms$({ algorithm }, { account$: SomeAccount$ }).subscribe(result); @@ -325,7 +366,12 @@ describe("GeneratorMetadataProvider", () => { [Type.password, [password, passphrase]], ])("gets a category of algorithms", async (category, metadata) => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, metadata); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + metadata, + ); const result = new ReplaySubject(1); provider.algorithms$({ type: category }, { account$: SomeAccount$ }).subscribe(result); @@ -344,7 +390,12 @@ describe("GeneratorMetadataProvider", () => { } as any); SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([policy])); const metadata = [password, passphrase]; - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, metadata); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + metadata, + ); const algorithmResult = new ReplaySubject(1); const categoryResult = new ReplaySubject(1); @@ -361,9 +412,12 @@ describe("GeneratorMetadataProvider", () => { it("omits algorithms whose metadata is unavailable", async () => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [ - password, - ]); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [password], + ); const algorithmResult = new ReplaySubject(1); const categoryResult = new ReplaySubject(1); @@ -379,7 +433,12 @@ describe("GeneratorMetadataProvider", () => { }); it("panics when neither algorithm nor category are specified", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); expect(() => provider.algorithms$({} as any, { account$: SomeAccount$ })).toThrow( "algorithm or type required", @@ -403,9 +462,12 @@ describe("GeneratorMetadataProvider", () => { [Type.password, password], ])("emits the user's %s preference", async (type, metadata) => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [ - metadata, - ]); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [metadata], + ); const result = new ReplaySubject(1); provider.preference$(type, { account$: SomeAccount$ }).subscribe(result); @@ -415,9 +477,12 @@ describe("GeneratorMetadataProvider", () => { it("emits a default when the user's preference is unavailable", async () => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [ - plusAddress, - ]); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [plusAddress], + ); const result = new ReplaySubject(1); // precondition: the preferred email is excluded from the provided metadata @@ -430,7 +495,12 @@ describe("GeneratorMetadataProvider", () => { it("emits the original preference when the user's preference is unavailable and there is no metadata", async () => { SomePolicyService.policiesByType$.mockReturnValue(new BehaviorSubject([])); - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const result = new ReplaySubject(1); provider.preference$(Type.email, { account$: SomeAccount$ }).subscribe(result); @@ -441,7 +511,12 @@ describe("GeneratorMetadataProvider", () => { describe("preferences", () => { it("returns a user state subject", () => { - const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []); + const provider = new GeneratorMetadataProvider( + SystemProvider, + SomePolicyService, + SomeExtensionService, + [], + ); const subject = provider.preferences({ account$: SomeAccount$ }); diff --git a/libs/tools/generator/core/src/providers/generator-profile-provider.spec.ts b/libs/tools/generator/core/src/providers/generator-profile-provider.spec.ts index 32d99aa8a1f..360f42e9fb5 100644 --- a/libs/tools/generator/core/src/providers/generator-profile-provider.spec.ts +++ b/libs/tools/generator/core/src/providers/generator-profile-provider.spec.ts @@ -8,12 +8,12 @@ import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; import { LegacyEncryptorProvider } from "@bitwarden/common/tools/cryptography/legacy-encryptor-provider"; import { UserEncryptor } from "@bitwarden/common/tools/cryptography/user-encryptor.abstraction"; -import { disabledSemanticLoggerProvider } from "@bitwarden/common/tools/log"; import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; import { IdentityConstraint } from "@bitwarden/common/tools/state/identity-state-constraint"; import { UserStateSubjectDependencyProvider } from "@bitwarden/common/tools/state/user-state-subject-dependency-provider"; import { StateConstraints } from "@bitwarden/common/tools/types"; import { OrganizationId, PolicyId, UserId } from "@bitwarden/common/types/guid"; +import { disabledSemanticLoggerProvider } from "@bitwarden/logging"; import { FakeStateProvider, FakeAccountService, awaitAsync } from "../../../../../common/spec"; import { CoreProfileMetadata, ProfileContext } from "../metadata/profile-metadata"; diff --git a/libs/tools/generator/core/src/providers/generator-profile-provider.ts b/libs/tools/generator/core/src/providers/generator-profile-provider.ts index 4117d1f2a78..c1a3bc57189 100644 --- a/libs/tools/generator/core/src/providers/generator-profile-provider.ts +++ b/libs/tools/generator/core/src/providers/generator-profile-provider.ts @@ -12,10 +12,10 @@ import { import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { BoundDependency } from "@bitwarden/common/tools/dependencies"; -import { SemanticLogger } from "@bitwarden/common/tools/log"; import { anyComplete } from "@bitwarden/common/tools/rx"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; import { UserStateSubjectDependencyProvider } from "@bitwarden/common/tools/state/user-state-subject-dependency-provider"; +import { SemanticLogger } from "@bitwarden/logging"; import { ProfileContext, CoreProfileMetadata, ProfileMetadata } from "../metadata"; import { GeneratorConstraints } from "../types/generator-constraints"; diff --git a/libs/tools/generator/core/src/services/default-credential-generator.service.spec.ts b/libs/tools/generator/core/src/services/default-credential-generator.service.spec.ts index 81e7ae6ac63..04ca226b110 100644 --- a/libs/tools/generator/core/src/services/default-credential-generator.service.spec.ts +++ b/libs/tools/generator/core/src/services/default-credential-generator.service.spec.ts @@ -1,12 +1,12 @@ +import { mock } from "jest-mock-extended"; import { BehaviorSubject, Subject, firstValueFrom, of } from "rxjs"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; -import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { Site, VendorId } from "@bitwarden/common/tools/extension"; import { Bitwarden } from "@bitwarden/common/tools/extension/vendor/bitwarden"; import { Vendor } from "@bitwarden/common/tools/extension/vendor/data"; -import { SemanticLogger, ifEnabledSemanticLoggerProvider } from "@bitwarden/common/tools/log"; import { UserId } from "@bitwarden/common/types/guid"; +import { LogProvider, SemanticLogger } from "@bitwarden/logging"; import { awaitAsync } from "../../../../../common/spec"; import { @@ -40,16 +40,20 @@ type MockTwoLevelPartial = { describe("DefaultCredentialGeneratorService", () => { let service: DefaultCredentialGeneratorService; let providers: MockTwoLevelPartial; - let system: any; - let log: SemanticLogger; let mockExtension: { settings: jest.Mock }; + let mockLogger: SemanticLogger; + let logProvider: LogProvider; let account: Account; let createService: (overrides?: any) => DefaultCredentialGeneratorService; beforeEach(() => { - log = ifEnabledSemanticLoggerProvider(false, new ConsoleLogService(true), { - from: "DefaultCredentialGeneratorService tests", - }); + mockLogger = mock(); + // Override panic to throw errors as expected by tests + mockLogger.panic = jest.fn((context: any, msg?: string) => { + const errorMsg = msg || (typeof context === "string" ? context : context?.message || "panic"); + throw new Error(errorMsg); + }) as any; + logProvider = () => mockLogger; mockExtension = { settings: jest.fn() }; @@ -61,11 +65,6 @@ describe("DefaultCredentialGeneratorService", () => { name: "Test User", }; - system = { - log: jest.fn().mockReturnValue(log), - extension: mockExtension, - }; - providers = { metadata: { metadata: jest.fn(), @@ -87,7 +86,11 @@ describe("DefaultCredentialGeneratorService", () => { // similar to how the overrides are applied const providersCast = providers as unknown as CredentialGeneratorProviders; - const instance = new DefaultCredentialGeneratorService(providersCast, system); + const instance = new DefaultCredentialGeneratorService( + providersCast, + mockExtension as any, + logProvider, + ); Object.assign(instance, overrides); return instance; }; diff --git a/libs/tools/generator/core/src/services/default-credential-generator.service.ts b/libs/tools/generator/core/src/services/default-credential-generator.service.ts index 453139d284c..4a1c98c35e6 100644 --- a/libs/tools/generator/core/src/services/default-credential-generator.service.ts +++ b/libs/tools/generator/core/src/services/default-credential-generator.service.ts @@ -18,10 +18,10 @@ import { import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { BoundDependency, OnDependency } from "@bitwarden/common/tools/dependencies"; import { VendorId } from "@bitwarden/common/tools/extension"; -import { SemanticLogger } from "@bitwarden/common/tools/log"; -import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; +import { ExtensionService } from "@bitwarden/common/tools/extension/extension.service"; import { anyComplete, memoizedMap } from "@bitwarden/common/tools/rx"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; +import { LogProvider, SemanticLogger } from "@bitwarden/logging"; import { CredentialGeneratorService } from "../abstractions"; import { @@ -43,13 +43,15 @@ const THREE_MINUTES = 3 * 60 * 1000; export class DefaultCredentialGeneratorService implements CredentialGeneratorService { /** Instantiate the `DefaultCredentialGeneratorService`. * @param provide application services required by the credential generator. - * @param system low-level services required by the credential generator. + * @param extension service for managing forwarder extensions. + * @param log factory for creating semantic loggers. */ constructor( private readonly provide: CredentialGeneratorProviders, - private readonly system: SystemServiceProvider, + private readonly extension: ExtensionService, + log: LogProvider, ) { - this.log = system.log({ type: "DefaultCredentialGeneratorService" }); + this.log = log({ type: "DefaultCredentialGeneratorService" }); } private readonly log: SemanticLogger; @@ -192,7 +194,7 @@ export class DefaultCredentialGeneratorService implements CredentialGeneratorSer } this.log.info({ profile, vendor, site: activeProfile.site }, "loading extension profile"); - settings = this.system.extension.settings(activeProfile, vendor, dependencies); + settings = this.extension.settings(activeProfile, vendor, dependencies); } else { this.log.info({ profile, algorithm: metadata.id }, "loading generator profile"); settings = this.provide.profile.settings(activeProfile, dependencies);