opticedge-cloud-utils 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js CHANGED
@@ -2,28 +2,29 @@ module.exports = {
2
2
  root: true,
3
3
  parser: '@typescript-eslint/parser',
4
4
  parserOptions: {
5
- ecmaVersion: 2020, // modern JS features
6
- sourceType: 'module',
5
+ ecmaVersion: 2020, // modern JS features
6
+ sourceType: 'module'
7
7
  },
8
8
  env: {
9
9
  node: true,
10
10
  jest: true,
11
- es2020: true,
11
+ es2020: true
12
12
  },
13
13
  plugins: ['@typescript-eslint', 'jest'],
14
14
  extends: [
15
15
  'eslint:recommended',
16
16
  'plugin:@typescript-eslint/recommended',
17
17
  'plugin:jest/recommended',
18
+ 'plugin:prettier/recommended'
18
19
  ],
19
20
  rules: {
20
21
  // Your custom rules here, for example:
21
22
  'no-console': 'warn',
22
- '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
23
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
23
24
  },
24
25
  settings: {
25
26
  jest: {
26
- version: 29, // or your jest version
27
- },
28
- },
29
- };
27
+ version: 29 // or your jest version
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ build
4
+ coverage
package/.prettierrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "singleQuote": true,
3
+ "semi": false,
4
+ "trailingComma": "none",
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "arrowParens": "avoid"
8
+ }
@@ -12,7 +12,7 @@ async function verifyRequest(req, options) {
12
12
  try {
13
13
  const ticket = await client.verifyIdToken({
14
14
  idToken: token,
15
- audience: options.allowedAudience,
15
+ audience: options.allowedAudience
16
16
  });
17
17
  const payload = ticket.getPayload();
18
18
  return payload?.email === options.allowedServiceAccount;
@@ -9,70 +9,70 @@ jest.mock('google-auth-library', () => {
9
9
  if (idToken === 'valid-token' && audience === 'audience') {
10
10
  return {
11
11
  getPayload: () => ({
12
- email: 'allowed@example.com',
13
- }),
12
+ email: 'allowed@example.com'
13
+ })
14
14
  };
15
15
  }
16
16
  else if (idToken === 'wrong-email-token' && audience === 'audience') {
17
17
  return {
18
18
  getPayload: () => ({
19
- email: 'unauthorized@example.com',
20
- }),
19
+ email: 'unauthorized@example.com'
20
+ })
21
21
  };
22
22
  }
23
23
  throw new Error('Invalid token');
24
- }),
25
- })),
24
+ })
25
+ }))
26
26
  };
27
27
  });
28
28
  describe('verifyRequest', () => {
29
29
  it('returns true for valid token and matching email', async () => {
30
30
  const mockReq = {
31
31
  headers: {
32
- authorization: 'Bearer valid-token',
33
- },
32
+ authorization: 'Bearer valid-token'
33
+ }
34
34
  };
35
35
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
36
  const result = await (0, verify_1.verifyRequest)(mockReq, {
37
37
  allowedAudience: 'audience',
38
- allowedServiceAccount: 'allowed@example.com',
38
+ allowedServiceAccount: 'allowed@example.com'
39
39
  });
40
40
  expect(result).toBe(true);
41
41
  });
42
42
  it('returns false for invalid token', async () => {
43
43
  const mockReq = {
44
44
  headers: {
45
- authorization: 'Bearer invalid-token',
46
- },
45
+ authorization: 'Bearer invalid-token'
46
+ }
47
47
  };
48
48
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
49
  const result = await (0, verify_1.verifyRequest)(mockReq, {
50
50
  allowedAudience: 'audience',
51
- allowedServiceAccount: 'allowed@example.com',
51
+ allowedServiceAccount: 'allowed@example.com'
52
52
  });
53
53
  expect(result).toBe(false);
54
54
  });
55
55
  it('returns false for missing authorization header', async () => {
56
56
  const mockReq = {
57
- headers: {},
57
+ headers: {}
58
58
  };
59
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
60
  const result = await (0, verify_1.verifyRequest)(mockReq, {
61
61
  allowedAudience: 'audience',
62
- allowedServiceAccount: 'allowed@example.com',
62
+ allowedServiceAccount: 'allowed@example.com'
63
63
  });
64
64
  expect(result).toBe(false);
65
65
  });
66
66
  it('returns false for incorrect email', async () => {
67
67
  const mockReq = {
68
68
  headers: {
69
- authorization: 'Bearer wrong-email-token',
70
- },
69
+ authorization: 'Bearer wrong-email-token'
70
+ }
71
71
  };
72
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
73
  const result = await (0, verify_1.verifyRequest)(mockReq, {
74
74
  allowedAudience: 'audience',
75
- allowedServiceAccount: 'allowed@example.com',
75
+ allowedServiceAccount: 'allowed@example.com'
76
76
  });
77
77
  expect(result).toBe(false);
78
78
  });
@@ -0,0 +1,3 @@
1
+ import { Collection, Document } from 'mongodb';
2
+ export declare function connectToMongo(projectId: string, uriSecret: string, dbName: string): Promise<void>;
3
+ export declare function getCollection<T extends Document = Document>(name: string): Collection<T>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectToMongo = connectToMongo;
4
+ exports.getCollection = getCollection;
5
+ const mongodb_1 = require("mongodb");
6
+ const secrets_1 = require("../utils/secrets"); // reuse your secret manager util
7
+ let client;
8
+ let db;
9
+ async function connectToMongo(projectId, uriSecret, dbName) {
10
+ if (client)
11
+ return;
12
+ const uri = await (0, secrets_1.getSecret)(projectId, uriSecret);
13
+ client = new mongodb_1.MongoClient(uri);
14
+ await client.connect();
15
+ db = client.db(dbName); // or pass DB name if needed
16
+ }
17
+ function getCollection(name) {
18
+ if (!db)
19
+ throw new Error('Mongo not initialized');
20
+ return db.collection(name);
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const mongo_1 = require("./mongo");
37
+ const mongodb_1 = require("mongodb");
38
+ const secretsModule = __importStar(require("../utils/secrets"));
39
+ jest.mock('mongodb');
40
+ jest.mock('../utils/secrets');
41
+ const mockDb = {
42
+ collection: jest.fn()
43
+ };
44
+ const mockConnect = jest.fn();
45
+ const mockClient = {
46
+ connect: mockConnect,
47
+ db: jest.fn().mockReturnValue(mockDb)
48
+ };
49
+ beforeEach(() => {
50
+ jest.clearAllMocks();
51
+ secretsModule.getSecret.mockResolvedValue('mongodb://mock-uri');
52
+ mongodb_1.MongoClient.mockImplementation(() => mockClient);
53
+ });
54
+ describe('Mongo Utils', () => {
55
+ it('should connect to MongoDB and store client/db', async () => {
56
+ await (0, mongo_1.connectToMongo)('test-project', 'mongo-uri-secret', 'test-db');
57
+ expect(secretsModule.getSecret).toHaveBeenCalledWith('test-project', 'mongo-uri-secret');
58
+ expect(mockConnect).toHaveBeenCalled();
59
+ expect(mockClient.db).toHaveBeenCalledWith('test-db');
60
+ });
61
+ it('should return a collection when db is initialized', async () => {
62
+ await (0, mongo_1.connectToMongo)('test-project', 'mongo-uri-secret', 'test-db');
63
+ (0, mongo_1.getCollection)('users');
64
+ expect(mockDb.collection).toHaveBeenCalledWith('users');
65
+ });
66
+ it('should throw error if db is not initialized', () => {
67
+ jest.isolateModules(() => {
68
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
69
+ const { getCollection } = require('./mongo');
70
+ expect(() => getCollection('users')).toThrow('Mongo not initialized');
71
+ });
72
+ });
73
+ });
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './auth/verify';
2
2
  export * from './utils/secrets';
3
+ export * from './db/mongo';
package/dist/index.js CHANGED
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./auth/verify"), exports);
18
18
  __exportStar(require("./utils/secrets"), exports);
19
+ __exportStar(require("./db/mongo"), exports);
@@ -16,7 +16,7 @@ async function getSecret(projectId, secretName) {
16
16
  throw new Error('secretName is required');
17
17
  const secretClient = new secret_manager_1.SecretManagerServiceClient();
18
18
  const [version] = await secretClient.accessSecretVersion({
19
- name: `projects/${projectId}/secrets/${secretName}/versions/latest`,
19
+ name: `projects/${projectId}/secrets/${secretName}/versions/latest`
20
20
  });
21
21
  return version.payload?.data?.toString('utf-8') ?? '';
22
22
  }
@@ -6,7 +6,7 @@ jest.mock('@google-cloud/secret-manager');
6
6
  // Mock implementation
7
7
  const mockAccessSecretVersion = jest.fn();
8
8
  secret_manager_1.SecretManagerServiceClient.mockImplementation(() => ({
9
- accessSecretVersion: mockAccessSecretVersion,
9
+ accessSecretVersion: mockAccessSecretVersion
10
10
  }));
11
11
  describe('getSecret', () => {
12
12
  beforeEach(() => {
@@ -15,13 +15,13 @@ describe('getSecret', () => {
15
15
  it('returns secret value as string', async () => {
16
16
  mockAccessSecretVersion.mockResolvedValue([
17
17
  {
18
- payload: { data: Buffer.from('super-secret-value') },
19
- },
18
+ payload: { data: Buffer.from('super-secret-value') }
19
+ }
20
20
  ]);
21
21
  const result = await (0, secrets_1.getSecret)('test-project', 'test-secret');
22
22
  expect(result).toBe('super-secret-value');
23
23
  expect(mockAccessSecretVersion).toHaveBeenCalledWith({
24
- name: 'projects/test-project/secrets/test-secret/versions/latest',
24
+ name: 'projects/test-project/secrets/test-secret/versions/latest'
25
25
  });
26
26
  });
27
27
  it('throws if projectId is missing', async () => {
package/jest.config.js CHANGED
@@ -1,12 +1,12 @@
1
- const { createDefaultPreset } = require("ts-jest");
1
+ const { createDefaultPreset } = require('ts-jest')
2
2
 
3
- const tsJestTransformCfg = createDefaultPreset().transform;
3
+ const tsJestTransformCfg = createDefaultPreset().transform
4
4
 
5
5
  /** @type {import("jest").Config} **/
6
6
  module.exports = {
7
- testEnvironment: "node",
7
+ testEnvironment: 'node',
8
8
  transform: {
9
- ...tsJestTransformCfg,
9
+ ...tsJestTransformCfg
10
10
  },
11
- testPathIgnorePatterns: ['/node_modules/', '/dist/'],
12
- };
11
+ testPathIgnorePatterns: ['/node_modules/', '/dist/']
12
+ }
package/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "opticedge-cloud-utils",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Common utilities for cloud functions",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "test": "jest --coverage",
10
- "lint": "eslint . --ext .ts",
10
+ "lint": "eslint . --ext .ts --fix",
11
+ "format": "prettier --write .",
12
+ "fix": "npm run lint && npm run format",
11
13
  "prepare": "npm run build"
12
14
  },
13
15
  "author": "Evans Musonda",
14
16
  "license": "MIT",
15
17
  "dependencies": {
16
18
  "@google-cloud/secret-manager": "^6.0.1",
17
- "google-auth-library": "^9.15.1"
19
+ "google-auth-library": "^9.15.1",
20
+ "mongodb": "^6.16.0"
18
21
  },
19
22
  "devDependencies": {
20
23
  "@google-cloud/functions-framework": "^4.0.0",
@@ -23,8 +26,11 @@
23
26
  "@typescript-eslint/eslint-plugin": "^8.33.0",
24
27
  "@typescript-eslint/parser": "^8.33.0",
25
28
  "eslint": "^8.57.1",
29
+ "eslint-config-prettier": "^10.1.5",
26
30
  "eslint-plugin-jest": "^28.11.1",
31
+ "eslint-plugin-prettier": "^5.4.0",
27
32
  "jest": "^29.7.0",
33
+ "prettier": "^3.5.3",
28
34
  "ts-jest": "^29.3.4",
29
35
  "typescript": "^5.8.3"
30
36
  }
@@ -1,4 +1,4 @@
1
- import { verifyRequest } from './verify';
1
+ import { verifyRequest } from './verify'
2
2
 
3
3
  // Mock the google-auth-library
4
4
  jest.mock('google-auth-library', () => {
@@ -8,82 +8,82 @@ jest.mock('google-auth-library', () => {
8
8
  if (idToken === 'valid-token' && audience === 'audience') {
9
9
  return {
10
10
  getPayload: () => ({
11
- email: 'allowed@example.com',
12
- }),
13
- };
11
+ email: 'allowed@example.com'
12
+ })
13
+ }
14
14
  } else if (idToken === 'wrong-email-token' && audience === 'audience') {
15
15
  return {
16
16
  getPayload: () => ({
17
- email: 'unauthorized@example.com',
18
- }),
19
- };
17
+ email: 'unauthorized@example.com'
18
+ })
19
+ }
20
20
  }
21
- throw new Error('Invalid token');
22
- }),
23
- })),
24
- };
25
- });
21
+ throw new Error('Invalid token')
22
+ })
23
+ }))
24
+ }
25
+ })
26
26
 
27
27
  describe('verifyRequest', () => {
28
28
  it('returns true for valid token and matching email', async () => {
29
29
  const mockReq = {
30
30
  headers: {
31
- authorization: 'Bearer valid-token',
32
- },
33
- };
31
+ authorization: 'Bearer valid-token'
32
+ }
33
+ }
34
34
 
35
35
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
36
  const result = await verifyRequest(mockReq as any, {
37
37
  allowedAudience: 'audience',
38
- allowedServiceAccount: 'allowed@example.com',
39
- });
38
+ allowedServiceAccount: 'allowed@example.com'
39
+ })
40
40
 
41
- expect(result).toBe(true);
42
- });
41
+ expect(result).toBe(true)
42
+ })
43
43
 
44
44
  it('returns false for invalid token', async () => {
45
45
  const mockReq = {
46
46
  headers: {
47
- authorization: 'Bearer invalid-token',
48
- },
49
- };
47
+ authorization: 'Bearer invalid-token'
48
+ }
49
+ }
50
50
 
51
51
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
52
  const result = await verifyRequest(mockReq as any, {
53
53
  allowedAudience: 'audience',
54
- allowedServiceAccount: 'allowed@example.com',
55
- });
54
+ allowedServiceAccount: 'allowed@example.com'
55
+ })
56
56
 
57
- expect(result).toBe(false);
58
- });
57
+ expect(result).toBe(false)
58
+ })
59
59
 
60
60
  it('returns false for missing authorization header', async () => {
61
61
  const mockReq = {
62
- headers: {},
63
- };
62
+ headers: {}
63
+ }
64
64
 
65
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
66
  const result = await verifyRequest(mockReq as any, {
67
67
  allowedAudience: 'audience',
68
- allowedServiceAccount: 'allowed@example.com',
69
- });
68
+ allowedServiceAccount: 'allowed@example.com'
69
+ })
70
70
 
71
- expect(result).toBe(false);
72
- });
71
+ expect(result).toBe(false)
72
+ })
73
73
 
74
74
  it('returns false for incorrect email', async () => {
75
75
  const mockReq = {
76
76
  headers: {
77
- authorization: 'Bearer wrong-email-token',
78
- },
79
- };
77
+ authorization: 'Bearer wrong-email-token'
78
+ }
79
+ }
80
80
 
81
81
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
82
  const result = await verifyRequest(mockReq as any, {
83
83
  allowedAudience: 'audience',
84
- allowedServiceAccount: 'allowed@example.com',
85
- });
84
+ allowedServiceAccount: 'allowed@example.com'
85
+ })
86
86
 
87
- expect(result).toBe(false);
88
- });
89
- });
87
+ expect(result).toBe(false)
88
+ })
89
+ })
@@ -1,32 +1,29 @@
1
- import { OAuth2Client } from 'google-auth-library';
2
- import { Request } from '@google-cloud/functions-framework';
1
+ import { OAuth2Client } from 'google-auth-library'
2
+ import { Request } from '@google-cloud/functions-framework'
3
3
 
4
4
  interface VerifyOptions {
5
- allowedAudience: string;
6
- allowedServiceAccount: string;
5
+ allowedAudience: string
6
+ allowedServiceAccount: string
7
7
  }
8
8
 
9
- export async function verifyRequest(
10
- req: Request,
11
- options: VerifyOptions
12
- ): Promise<boolean> {
13
- const authHeader = req.headers['authorization'];
9
+ export async function verifyRequest(req: Request, options: VerifyOptions): Promise<boolean> {
10
+ const authHeader = req.headers['authorization']
14
11
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
15
- return false;
12
+ return false
16
13
  }
17
14
 
18
- const token = authHeader.split(' ')[1];
19
- const client = new OAuth2Client();
15
+ const token = authHeader.split(' ')[1]
16
+ const client = new OAuth2Client()
20
17
 
21
18
  try {
22
19
  const ticket = await client.verifyIdToken({
23
20
  idToken: token,
24
- audience: options.allowedAudience,
25
- });
21
+ audience: options.allowedAudience
22
+ })
26
23
 
27
- const payload = ticket.getPayload();
28
- return payload?.email === options.allowedServiceAccount;
24
+ const payload = ticket.getPayload()
25
+ return payload?.email === options.allowedServiceAccount
29
26
  } catch {
30
- return false;
27
+ return false
31
28
  }
32
29
  }
@@ -0,0 +1,47 @@
1
+ import { connectToMongo, getCollection } from './mongo'
2
+ import { MongoClient, Db } from 'mongodb'
3
+ import * as secretsModule from '../utils/secrets'
4
+
5
+ jest.mock('mongodb')
6
+ jest.mock('../utils/secrets')
7
+
8
+ const mockDb = {
9
+ collection: jest.fn()
10
+ } as unknown as Db
11
+
12
+ const mockConnect = jest.fn()
13
+ const mockClient = {
14
+ connect: mockConnect,
15
+ db: jest.fn().mockReturnValue(mockDb)
16
+ } as unknown as MongoClient
17
+
18
+ beforeEach(() => {
19
+ jest.clearAllMocks()
20
+ ;(secretsModule.getSecret as jest.Mock).mockResolvedValue('mongodb://mock-uri')
21
+ ;(MongoClient as unknown as jest.Mock).mockImplementation(() => mockClient)
22
+ })
23
+
24
+ describe('Mongo Utils', () => {
25
+ it('should connect to MongoDB and store client/db', async () => {
26
+ await connectToMongo('test-project', 'mongo-uri-secret', 'test-db')
27
+
28
+ expect(secretsModule.getSecret).toHaveBeenCalledWith('test-project', 'mongo-uri-secret')
29
+ expect(mockConnect).toHaveBeenCalled()
30
+ expect(mockClient.db).toHaveBeenCalledWith('test-db')
31
+ })
32
+
33
+ it('should return a collection when db is initialized', async () => {
34
+ await connectToMongo('test-project', 'mongo-uri-secret', 'test-db')
35
+ getCollection('users')
36
+
37
+ expect(mockDb.collection).toHaveBeenCalledWith('users')
38
+ })
39
+
40
+ it('should throw error if db is not initialized', () => {
41
+ jest.isolateModules(() => {
42
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
43
+ const { getCollection } = require('./mongo')
44
+ expect(() => getCollection('users')).toThrow('Mongo not initialized')
45
+ })
46
+ })
47
+ })
@@ -0,0 +1,19 @@
1
+ import { MongoClient, Db, Collection, Document } from 'mongodb'
2
+ import { getSecret } from '../utils/secrets' // reuse your secret manager util
3
+
4
+ let client: MongoClient
5
+ let db: Db
6
+
7
+ export async function connectToMongo(projectId: string, uriSecret: string, dbName: string) {
8
+ if (client) return
9
+
10
+ const uri = await getSecret(projectId, uriSecret)
11
+ client = new MongoClient(uri)
12
+ await client.connect()
13
+ db = client.db(dbName) // or pass DB name if needed
14
+ }
15
+
16
+ export function getCollection<T extends Document = Document>(name: string): Collection<T> {
17
+ if (!db) throw new Error('Mongo not initialized')
18
+ return db.collection<T>(name)
19
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
- export * from './auth/verify';
2
- export * from './utils/secrets';
1
+ export * from './auth/verify'
2
+ export * from './utils/secrets'
3
+ export * from './db/mongo'
@@ -1,44 +1,44 @@
1
- import { getSecret } from './secrets';
2
- import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
1
+ import { getSecret } from './secrets'
2
+ import { SecretManagerServiceClient } from '@google-cloud/secret-manager'
3
3
 
4
- jest.mock('@google-cloud/secret-manager');
4
+ jest.mock('@google-cloud/secret-manager')
5
5
 
6
6
  // Mock implementation
7
- const mockAccessSecretVersion = jest.fn();
8
- (SecretManagerServiceClient as unknown as jest.Mock).mockImplementation(() => ({
9
- accessSecretVersion: mockAccessSecretVersion,
10
- }));
7
+ const mockAccessSecretVersion = jest.fn()
8
+ ;(SecretManagerServiceClient as unknown as jest.Mock).mockImplementation(() => ({
9
+ accessSecretVersion: mockAccessSecretVersion
10
+ }))
11
11
 
12
12
  describe('getSecret', () => {
13
13
  beforeEach(() => {
14
- jest.clearAllMocks();
15
- });
14
+ jest.clearAllMocks()
15
+ })
16
16
 
17
17
  it('returns secret value as string', async () => {
18
18
  mockAccessSecretVersion.mockResolvedValue([
19
19
  {
20
- payload: { data: Buffer.from('super-secret-value') },
21
- },
22
- ]);
20
+ payload: { data: Buffer.from('super-secret-value') }
21
+ }
22
+ ])
23
23
 
24
- const result = await getSecret('test-project', 'test-secret');
25
- expect(result).toBe('super-secret-value');
24
+ const result = await getSecret('test-project', 'test-secret')
25
+ expect(result).toBe('super-secret-value')
26
26
  expect(mockAccessSecretVersion).toHaveBeenCalledWith({
27
- name: 'projects/test-project/secrets/test-secret/versions/latest',
28
- });
29
- });
27
+ name: 'projects/test-project/secrets/test-secret/versions/latest'
28
+ })
29
+ })
30
30
 
31
31
  it('throws if projectId is missing', async () => {
32
- await expect(getSecret('', 'secret')).rejects.toThrow('projectId is required');
33
- });
32
+ await expect(getSecret('', 'secret')).rejects.toThrow('projectId is required')
33
+ })
34
34
 
35
35
  it('throws if secretName is missing', async () => {
36
- await expect(getSecret('project', '')).rejects.toThrow('secretName is required');
37
- });
36
+ await expect(getSecret('project', '')).rejects.toThrow('secretName is required')
37
+ })
38
38
 
39
39
  it('returns empty string if payload or data is missing', async () => {
40
- mockAccessSecretVersion.mockResolvedValue([{}]); // no payload
41
- const result = await getSecret('project', 'secret');
42
- expect(result).toBe('');
43
- });
44
- });
40
+ mockAccessSecretVersion.mockResolvedValue([{}]) // no payload
41
+ const result = await getSecret('project', 'secret')
42
+ expect(result).toBe('')
43
+ })
44
+ })
@@ -1,4 +1,4 @@
1
- import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
1
+ import { SecretManagerServiceClient } from '@google-cloud/secret-manager'
2
2
 
3
3
  /**
4
4
  * Returns the latest value of a Secret Manager secret.
@@ -7,18 +7,15 @@ import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
7
7
  * @param secretName – Secret name (without version).
8
8
  * @returns – UTF-8 string value.
9
9
  */
10
- export async function getSecret(
11
- projectId: string,
12
- secretName: string
13
- ): Promise<string> {
14
- if (!projectId) throw new Error('projectId is required');
15
- if (!secretName) throw new Error('secretName is required');
10
+ export async function getSecret(projectId: string, secretName: string): Promise<string> {
11
+ if (!projectId) throw new Error('projectId is required')
12
+ if (!secretName) throw new Error('secretName is required')
16
13
 
17
- const secretClient = new SecretManagerServiceClient();
14
+ const secretClient = new SecretManagerServiceClient()
18
15
 
19
16
  const [version] = await secretClient.accessSecretVersion({
20
- name: `projects/${projectId}/secrets/${secretName}/versions/latest`,
21
- });
17
+ name: `projects/${projectId}/secrets/${secretName}/versions/latest`
18
+ })
22
19
 
23
- return (version.payload?.data as Buffer)?.toString('utf-8') ?? '';
20
+ return (version.payload?.data as Buffer)?.toString('utf-8') ?? ''
24
21
  }