mirror of
https://github.com/bitwarden/browser
synced 2026-02-09 05:00:10 +00:00
additional unit tests; fixes
This commit is contained in:
@@ -1,27 +1,40 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BehaviorSubject, ReplaySubject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
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 { LegacyEncryptorProvider } from "@bitwarden/common/tools/cryptography/legacy-encryptor-provider";
|
||||
import { UserEncryptor } from "@bitwarden/common/tools/cryptography/user-encryptor.abstraction";
|
||||
import { ExtensionMetadata, ExtensionSite, Site, SiteId, SiteMetadata } from "@bitwarden/common/tools/extension"
|
||||
import {
|
||||
ExtensionMetadata,
|
||||
ExtensionSite,
|
||||
Site,
|
||||
SiteId,
|
||||
SiteMetadata,
|
||||
} 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 { FakeAccountService, FakeStateProvider } from "../../../../../common/spec";
|
||||
import { Algorithm, AlgorithmsByType, Profile, Type, Types } from "../metadata";
|
||||
import { Algorithm, AlgorithmsByType, CredentialAlgorithm, Type, Types } from "../metadata";
|
||||
import catchall from "../metadata/email/catchall";
|
||||
import plusAddress from "../metadata/email/plus-address";
|
||||
import passphrase from "../metadata/password/eff-word-list";
|
||||
import password from "../metadata/password/random-password";
|
||||
import effWordList from "../metadata/username/eff-word-list";
|
||||
import { CredentialPreference } from "../types";
|
||||
|
||||
import { PREFERENCES } from "./credential-preferences";
|
||||
import { GeneratorMetadataProvider } from "./generator-metadata-provider";
|
||||
|
||||
|
||||
|
||||
const SomeUser = "some user" as UserId;
|
||||
const SomeAccount = {
|
||||
id: SomeUser,
|
||||
@@ -31,8 +44,6 @@ const SomeAccount = {
|
||||
};
|
||||
const SomeAccount$ = new BehaviorSubject<Account>(SomeAccount);
|
||||
|
||||
type TestType = { foo: string };
|
||||
|
||||
const SomeEncryptor: UserEncryptor = {
|
||||
userId: SomeUser,
|
||||
|
||||
@@ -73,18 +84,20 @@ const SomeSite: SiteMetadata = Object.freeze({
|
||||
availableFields: [],
|
||||
});
|
||||
|
||||
const SomePolicyService = mock<PolicyService>();
|
||||
|
||||
const ApplicationProvider = {
|
||||
/** Policy configured by the administrative console */
|
||||
policy: mock<PolicyService>(),
|
||||
policy: SomePolicyService,
|
||||
|
||||
/** Client extension metadata and profile access */
|
||||
extension: mock<ExtensionService>({
|
||||
site: () => new ExtensionSite(SomeSite, new Map())
|
||||
site: () => new ExtensionSite(SomeSite, new Map()),
|
||||
}),
|
||||
|
||||
/** Event monitoring and diagnostic interfaces */
|
||||
log: disabledSemanticLoggerProvider,
|
||||
} as SystemServiceProvider;
|
||||
} as SystemServiceProvider;
|
||||
|
||||
describe("GeneratorMetadataProvider", () => {
|
||||
beforeEach(() => {
|
||||
@@ -93,7 +106,9 @@ describe("GeneratorMetadataProvider", () => {
|
||||
|
||||
describe("metadata", () => {
|
||||
it("returns algorithm metadata", async () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [password]);
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [
|
||||
password,
|
||||
]);
|
||||
|
||||
const metadata = provider.metadata(password.id);
|
||||
|
||||
@@ -101,17 +116,17 @@ describe("GeneratorMetadataProvider", () => {
|
||||
});
|
||||
|
||||
it("returns forwarder metadata", async () => {
|
||||
const extensionMetadata : ExtensionMetadata = {
|
||||
const extensionMetadata: ExtensionMetadata = {
|
||||
site: SomeSite,
|
||||
product: { vendor: Bitwarden },
|
||||
host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" },
|
||||
requestedFields: []
|
||||
}
|
||||
requestedFields: [],
|
||||
};
|
||||
const application = {
|
||||
...ApplicationProvider,
|
||||
extension: mock<ExtensionService>({
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]]))
|
||||
})
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])),
|
||||
}),
|
||||
};
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, application, []);
|
||||
|
||||
@@ -129,7 +144,9 @@ describe("GeneratorMetadataProvider", () => {
|
||||
it("panics when an extension not found", async () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
|
||||
expect(() => provider.metadata({ forwarder: "not found" as any })).toThrow("extension not found");
|
||||
expect(() => provider.metadata({ forwarder: "not found" as any })).toThrow(
|
||||
"extension not found",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -147,7 +164,7 @@ describe("GeneratorMetadataProvider", () => {
|
||||
it("returns the password category's algorithms", () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
|
||||
const result = provider.algorithms({ category: Type.password });
|
||||
const result = provider.algorithms({ type: Type.password });
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(AlgorithmsByType[Type.password]));
|
||||
});
|
||||
@@ -155,7 +172,7 @@ describe("GeneratorMetadataProvider", () => {
|
||||
it("returns the username category's algorithms", () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
|
||||
const result = provider.algorithms({ category: Type.username });
|
||||
const result = provider.algorithms({ type: Type.username });
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(AlgorithmsByType[Type.username]));
|
||||
});
|
||||
@@ -163,29 +180,29 @@ describe("GeneratorMetadataProvider", () => {
|
||||
it("returns the email category's algorithms", () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
|
||||
const result = provider.algorithms({ category: Type.email });
|
||||
const result = provider.algorithms({ type: Type.email });
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining(AlgorithmsByType[Type.email]));
|
||||
});
|
||||
|
||||
it("includes forwarder vendors in the email category's algorithms", () => {
|
||||
const extensionMetadata : ExtensionMetadata = {
|
||||
const extensionMetadata: ExtensionMetadata = {
|
||||
site: SomeSite,
|
||||
product: { vendor: Bitwarden },
|
||||
host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" },
|
||||
requestedFields: []
|
||||
}
|
||||
requestedFields: [],
|
||||
};
|
||||
const application = {
|
||||
...ApplicationProvider,
|
||||
extension: mock<ExtensionService>({
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]]))
|
||||
})
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])),
|
||||
}),
|
||||
};
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, application, []);
|
||||
|
||||
const result = provider.algorithms({ category: Type.email });
|
||||
const result = provider.algorithms({ type: Type.email });
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining([{ forwarder: Bitwarden.id }]))
|
||||
expect(result).toEqual(expect.arrayContaining([{ forwarder: Bitwarden.id }]));
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -203,23 +220,23 @@ describe("GeneratorMetadataProvider", () => {
|
||||
});
|
||||
|
||||
it("returns explicit forwarders", () => {
|
||||
const extensionMetadata : ExtensionMetadata = {
|
||||
const extensionMetadata: ExtensionMetadata = {
|
||||
site: SomeSite,
|
||||
product: { vendor: Bitwarden },
|
||||
host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" },
|
||||
requestedFields: []
|
||||
}
|
||||
requestedFields: [],
|
||||
};
|
||||
const application = {
|
||||
...ApplicationProvider,
|
||||
extension: mock<ExtensionService>({
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]]))
|
||||
})
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])),
|
||||
}),
|
||||
};
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, application, []);
|
||||
|
||||
const result = provider.algorithms({ algorithm: { forwarder: Bitwarden.id } });
|
||||
|
||||
expect(result).toEqual(expect.arrayContaining([{ forwarder: Bitwarden.id }]))
|
||||
expect(result).toEqual(expect.arrayContaining([{ forwarder: Bitwarden.id }]));
|
||||
});
|
||||
|
||||
it("returns an empty array when the algorithm is invalid", () => {
|
||||
@@ -232,83 +249,162 @@ describe("GeneratorMetadataProvider", () => {
|
||||
});
|
||||
|
||||
it("returns an empty array when the forwarder is invalid", () => {
|
||||
const extensionMetadata : ExtensionMetadata = {
|
||||
const extensionMetadata: ExtensionMetadata = {
|
||||
site: SomeSite,
|
||||
product: { vendor: Bitwarden },
|
||||
host: { authentication: true, selfHost: "maybe", baseUrl: "https://www.example.com" },
|
||||
requestedFields: []
|
||||
}
|
||||
requestedFields: [],
|
||||
};
|
||||
const application = {
|
||||
...ApplicationProvider,
|
||||
extension: mock<ExtensionService>({
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]]))
|
||||
})
|
||||
site: () => new ExtensionSite(SomeSite, new Map([[Bitwarden.id, extensionMetadata]])),
|
||||
}),
|
||||
};
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, application, []);
|
||||
|
||||
// `any` cast required because this test subverts the type system
|
||||
const result = provider.algorithms({ algorithm: { forwarder: "an invalid forwarder" as any } });
|
||||
const result = provider.algorithms({
|
||||
algorithm: { forwarder: "an invalid forwarder" as any },
|
||||
});
|
||||
|
||||
expect(result).toEqual([])
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("panics when neither an algorithm nor a category is specified", () => {
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
|
||||
// `any` cast required because this test subverts the type system
|
||||
expect(() => provider.algorithms({} as any)).toThrow('algorithm or category required');
|
||||
expect(() => provider.algorithms({} as any)).toThrow("algorithm or category required");
|
||||
});
|
||||
});
|
||||
|
||||
describe("algorithms$", () => {
|
||||
it.each([
|
||||
[Algorithm.catchall, catchall],
|
||||
[Algorithm.username, effWordList],
|
||||
[Algorithm.password, password],
|
||||
])("gets a specific algorithm", async (algorithm, metadata) => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [
|
||||
metadata,
|
||||
]);
|
||||
const result = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
|
||||
provider.algorithms$({ algorithm }, { account$: SomeAccount$ }).subscribe(result);
|
||||
|
||||
await expect(firstValueFrom(result)).resolves.toEqual([algorithm]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[Type.email, [catchall, plusAddress]],
|
||||
[Type.username, [effWordList]],
|
||||
[Type.password, [password, passphrase]],
|
||||
])("gets a category of algorithms", async (category, metadata) => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, metadata);
|
||||
const result = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
|
||||
provider.algorithms$({ type: category }, { account$: SomeAccount$ }).subscribe(result);
|
||||
|
||||
const expectedAlgorithms = expect.arrayContaining(metadata.map((m) => m.id));
|
||||
await expect(firstValueFrom(result)).resolves.toEqual(expectedAlgorithms);
|
||||
});
|
||||
|
||||
it("omits algorithms blocked by policy", async () => {
|
||||
const policy = new Policy({
|
||||
type: PolicyType.PasswordGenerator,
|
||||
enabled: true,
|
||||
data: {
|
||||
overridePasswordType: Algorithm.password,
|
||||
},
|
||||
} as any);
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([policy]));
|
||||
const metadata = [password, passphrase];
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, metadata);
|
||||
const algorithmResult = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
const categoryResult = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
|
||||
provider
|
||||
.algorithms$({ algorithm: Algorithm.passphrase }, { account$: SomeAccount$ })
|
||||
.subscribe(algorithmResult);
|
||||
provider
|
||||
.algorithms$({ type: Type.password }, { account$: SomeAccount$ })
|
||||
.subscribe(categoryResult);
|
||||
|
||||
await expect(firstValueFrom(algorithmResult)).resolves.toEqual([]);
|
||||
await expect(firstValueFrom(categoryResult)).resolves.toEqual([password.id]);
|
||||
});
|
||||
|
||||
it("omits algorithms whose metadata is unavailable", async () => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [
|
||||
password,
|
||||
]);
|
||||
const algorithmResult = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
const categoryResult = new ReplaySubject<CredentialAlgorithm[]>(1);
|
||||
|
||||
provider
|
||||
.algorithms$({ algorithm: Algorithm.passphrase }, { account$: SomeAccount$ })
|
||||
.subscribe(algorithmResult);
|
||||
provider
|
||||
.algorithms$({ type: Type.password }, { account$: SomeAccount$ })
|
||||
.subscribe(categoryResult);
|
||||
|
||||
await expect(firstValueFrom(algorithmResult)).resolves.toEqual([]);
|
||||
await expect(firstValueFrom(categoryResult)).resolves.toEqual([password.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("preference$", () => {
|
||||
it("", async () => {
|
||||
|
||||
const preferences: CredentialPreference = deepFreeze({
|
||||
[Type.email]: { algorithm: Algorithm.catchall, updated: new Date() },
|
||||
[Type.username]: { algorithm: Algorithm.username, updated: new Date() },
|
||||
[Type.password]: { algorithm: Algorithm.password, updated: new Date() },
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await SomeStateProvider.setUserState(PREFERENCES, preferences, SomeAccount.id);
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
it.each([
|
||||
[Type.email, catchall],
|
||||
[Type.username, effWordList],
|
||||
[Type.password, password],
|
||||
])("emits the user's %s preference", async (type, metadata) => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [
|
||||
metadata,
|
||||
]);
|
||||
const result = new ReplaySubject<CredentialAlgorithm | undefined>(1);
|
||||
|
||||
provider.preference$(type, { account$: SomeAccount$ }).subscribe(result);
|
||||
|
||||
await expect(firstValueFrom(result)).resolves.toEqual(preferences[type].algorithm);
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
it("emits a default when the user's preference is unavailable", async () => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, [
|
||||
plusAddress,
|
||||
]);
|
||||
const result = new ReplaySubject<CredentialAlgorithm | undefined>(1);
|
||||
|
||||
// precondition: the preferred email is excluded from the provided metadata
|
||||
expect(preferences.email.algorithm).not.toEqual(plusAddress.id);
|
||||
|
||||
provider.preference$(Type.email, { account$: SomeAccount$ }).subscribe(result);
|
||||
|
||||
await expect(firstValueFrom(result)).resolves.toEqual(plusAddress.id);
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
it("emits undefined when the user's preference is unavailable and there is no metadata", async () => {
|
||||
SomePolicyService.getAll$.mockReturnValue(new BehaviorSubject([]));
|
||||
const provider = new GeneratorMetadataProvider(SystemProvider, ApplicationProvider, []);
|
||||
const result = new ReplaySubject<CredentialAlgorithm | undefined>(1);
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("algorithm$", () => {
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
provider.preference$(Type.email, { account$: SomeAccount$ }).subscribe(result);
|
||||
|
||||
await expect(firstValueFrom(result)).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import { CredentialPreference } from "../types";
|
||||
import { PREFERENCES } from "./credential-preferences";
|
||||
|
||||
type AlgorithmRequest = { algorithm: CredentialAlgorithm };
|
||||
type TypeRequest = { category: CredentialType };
|
||||
type TypeRequest = { type: CredentialType };
|
||||
type MetadataRequest = Partial<AlgorithmRequest & TypeRequest>;
|
||||
|
||||
/** Surfaces contextual information to credential generators */
|
||||
@@ -69,7 +69,7 @@ export class GeneratorMetadataProvider {
|
||||
* @param algorithm identifies the algorithm
|
||||
* @returns the algorithm's generator metadata
|
||||
* @throws when the algorithm doesn't identify a known metadata entry
|
||||
*/
|
||||
*/
|
||||
metadata(algorithm: CredentialAlgorithm) {
|
||||
let result = null;
|
||||
if (isForwarderExtensionId(algorithm)) {
|
||||
@@ -91,7 +91,7 @@ export class GeneratorMetadataProvider {
|
||||
}
|
||||
|
||||
/** retrieve credential types */
|
||||
types() : ReadonlyArray<CredentialType> {
|
||||
types(): ReadonlyArray<CredentialType> {
|
||||
return Types;
|
||||
}
|
||||
|
||||
@@ -110,15 +110,17 @@ export class GeneratorMetadataProvider {
|
||||
algorithms(requested: TypeRequest): CredentialAlgorithm[];
|
||||
algorithms(requested: MetadataRequest): CredentialAlgorithm[] {
|
||||
let algorithms: CredentialAlgorithm[];
|
||||
if (requested.category) {
|
||||
if (requested.type) {
|
||||
let forwarders: CredentialAlgorithm[] = [];
|
||||
if (requested.category === Type.email) {
|
||||
if (requested.type === Type.email) {
|
||||
forwarders = Array.from(this.site.extensions.keys()).map((forwarder) => ({ forwarder }));
|
||||
}
|
||||
|
||||
algorithms = AlgorithmsByType[requested.category].concat(forwarders);
|
||||
algorithms = AlgorithmsByType[requested.type].concat(forwarders);
|
||||
} else if (requested.algorithm && isForwarderExtensionId(requested.algorithm)) {
|
||||
algorithms = this.site.extensions.has(requested.algorithm.forwarder) ? [requested.algorithm] : [];
|
||||
algorithms = this.site.extensions.has(requested.algorithm.forwarder)
|
||||
? [requested.algorithm]
|
||||
: [];
|
||||
} else if (requested.algorithm) {
|
||||
algorithms = Algorithms.includes(requested.algorithm) ? [requested.algorithm] : [];
|
||||
} else {
|
||||
@@ -139,12 +141,14 @@ export class GeneratorMetadataProvider {
|
||||
|
||||
const available$ = account$.pipe(
|
||||
switchMap((account) => {
|
||||
const policies$ = this.application.policy.getAll$(PolicyType.PasswordGenerator, account.id).pipe(
|
||||
map((p) => availableAlgorithms_vNext(p).filter(a => this._metadata.has(a))),
|
||||
map((p) => new Set()),
|
||||
// complete policy emissions otherwise `switchMap` holds `algorithms$` open indefinitely
|
||||
takeUntil(anyComplete(account$)),
|
||||
);
|
||||
const policies$ = this.application.policy
|
||||
.getAll$(PolicyType.PasswordGenerator, account.id)
|
||||
.pipe(
|
||||
map((p) => availableAlgorithms_vNext(p).filter((a) => this._metadata.has(a))),
|
||||
map((p) => new Set(p)),
|
||||
// complete policy emissions otherwise `switchMap` holds `available$` open indefinitely
|
||||
takeUntil(anyComplete(account$)),
|
||||
);
|
||||
return policies$;
|
||||
}),
|
||||
map((available) => (a: CredentialAlgorithm) => isForwarderExtensionId(a) || available.has(a)),
|
||||
@@ -179,11 +183,11 @@ export class GeneratorMetadataProvider {
|
||||
requested: MetadataRequest,
|
||||
dependencies: BoundDependency<"account", Account>,
|
||||
): Observable<CredentialAlgorithm[]> {
|
||||
if (requested.category) {
|
||||
const { category } = requested;
|
||||
if (requested.type) {
|
||||
const { type: category } = requested;
|
||||
|
||||
return this.isAvailable$(dependencies).pipe(
|
||||
map((isAvailable) => AlgorithmsByType[category].filter(isAvailable)),
|
||||
map((isAvailable) => this.algorithms({ type: category }).filter(isAvailable)),
|
||||
);
|
||||
} else if (requested.algorithm) {
|
||||
const { algorithm } = requested;
|
||||
@@ -195,29 +199,29 @@ export class GeneratorMetadataProvider {
|
||||
}
|
||||
}
|
||||
|
||||
preference$(credentialType: CredentialType, dependencies: BoundDependency<"account", Account>) {
|
||||
preference$(type: CredentialType, dependencies: BoundDependency<"account", Account>) {
|
||||
const account$ = dependencies.account$.pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
||||
|
||||
const algorithm$ = this.preferences({ account$ }).pipe(
|
||||
combineLatestWith(this.isAvailable$({ account$ })),
|
||||
map(([preferences, isAvailable]) => {
|
||||
const algorithm: CredentialAlgorithm = preferences[credentialType].algorithm;
|
||||
const algorithm: CredentialAlgorithm = preferences[type].algorithm;
|
||||
if (isAvailable(algorithm)) {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
const algorithms = AlgorithmsByType[credentialType];
|
||||
const algorithms = this.algorithms({ type: type });
|
||||
// `?? null` because logging types must be `Jsonify<T>`
|
||||
const defaultAlgorithm = algorithms.find(isAvailable) ?? null;
|
||||
this.log.debug(
|
||||
{ algorithm, defaultAlgorithm, credentialType },
|
||||
{ algorithm, defaultAlgorithm, credentialType: type },
|
||||
"preference not available; defaulting the generator algorithm",
|
||||
);
|
||||
|
||||
// `?? undefined` so that interface is ADR-14 compliant
|
||||
return defaultAlgorithm ?? undefined;
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
return algorithm$;
|
||||
|
||||
Reference in New Issue
Block a user