< Summary - Envilder Node.js SDK

Information
Class: src/sdks/nodejs/src/infrastructure/azure/azure-key-vault-secret-provider.ts
Assembly: Default
File(s): src/sdks/nodejs/src/infrastructure/azure/azure-key-vault-secret-provider.ts
Tag: 558_41035824231
Line coverage
100%
Covered lines: 24
Uncovered lines: 0
Coverable lines: 24
Total lines: 72
Line coverage: 100%
Branch coverage
93%
Covered branches: 15
Total branches: 16
Branch coverage: 93.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

src/sdks/nodejs/src/infrastructure/azure/azure-key-vault-secret-provider.ts

#LineLine coverage
 1import type { SecretClient } from '@azure/keyvault-secrets';
 2import type { ISecretProvider } from '../../domain/ports/secret-provider.js';
 3import { toAzureSecretName } from './azure-secret-name.js';
 4
 5/**
 6 * {@link ISecretProvider} backed by Azure Key Vault.
 7 *
 8 * Secrets are fetched in parallel. Secrets that return HTTP 404
 9 * are treated as missing and silently omitted from the result.
 10 * Each reference is mapped to its Key Vault name with
 11 * {@link toAzureSecretName}, the same rule the CLI uses.
 12 */
 13export class AzureKeyVaultSecretProvider implements ISecretProvider {
 14  private readonly secretClient: SecretClient;
 15
 16  constructor(secretClient: SecretClient) {
 917    if (!secretClient) {
 118      throw new Error('secretClient cannot be null');
 19    }
 820    this.secretClient = secretClient;
 21  }
 22
 23  async getSecrets(names: string[]): Promise<Map<string, string>> {
 924    const result = new Map<string, string>();
 925    if (names.length === 0) {
 126      return result;
 27    }
 28
 829    for (const name of names) {
 1430      if (!name?.trim()) {
 131        throw new Error('Secret name cannot be null or empty');
 32      }
 33    }
 734    const secretNames = names.map(toAzureSecretName);
 35
 736    const entries = await Promise.all(
 37      names.map(async (name, index) => {
 1238        const value = await this.fetchSecret(secretNames[index]);
 1139        return [name, value] as const;
 40      }),
 41    );
 42
 643    for (const [name, value] of entries) {
 1144      if (value !== null) {
 945        result.set(name, value);
 46      }
 47    }
 48
 649    return result;
 50  }
 51
 52  private async fetchSecret(name: string): Promise<string | null> {
 1253    try {
 1254      const response = await this.secretClient.getSecret(name);
 955      return response.value ?? null;
 56    } catch (error: unknown) {
 357      if (isNotFound(error)) {
 258        return null;
 59      }
 160      throw error;
 61    }
 62  }
 63}
 64
 65function isNotFound(error: unknown): boolean {
 366  return (
 67    typeof error === 'object' &&
 68    error !== null &&
 69    'statusCode' in error &&
 70    (error as { statusCode: number }).statusCode === 404
 71  );
 72}