Skip to content

refactor: remove GmailOAuthClient and googleapis dependency#64

Merged
weaponsforge merged 4 commits into
devfrom
refactor/SE-56
Apr 16, 2026
Merged

refactor: remove GmailOAuthClient and googleapis dependency#64
weaponsforge merged 4 commits into
devfrom
refactor/SE-56

Conversation

@weaponsforge
Copy link
Copy Markdown
Owner

@weaponsforge weaponsforge commented Apr 16, 2026

Summary

  • Removes the GmailOAuthClient class (lib/google/oauth2client.ts) and all supporting files (oauth2client.interface.ts, oauth2client.schema.ts, oauth2client.types.ts, oauth2client.test.ts)
  • Removes the googleapis npm package dependency entirely
  • Replaces the GmailOAuthClient parameter on send() and createTransport3LO() with a plain TransportOath2SchemaType object (new types/transport.schema.ts)
  • GmailOAuthClient no longer appears in the public API exports

Related Issue

Why googleapis was removed

Previously, GmailOAuthClient wrapped google.auth.OAuth2 from the googleapis package solely to call getAccessToken() — exchanging the refresh token for a short-lived access token — before passing that token to Nodemailer.

This was unnecessary: Nodemailer's built-in OAuth2 transport support handles token refresh natively when a refreshToken is supplied in the transport auth config (see Nodemailer OAuth2 docs). Nodemailer calls the Google token endpoint internally on each send, so there is no need to pre-fetch an access token or maintain a separate google.auth.OAuth2 client instance. Removing this layer eliminates ~1,000 lines of wrapper code and drops the googleapis package — a large dependency that pulls in the entire Google API Node.js client library and was used for nothing other than one getAccessToken() call.

Before / After

Before — callers had to instantiate GmailOAuthClient, optionally pre-generate an access token, and pass it into send():

import { GmailOAuthClient, send } from '@weaponsforge/sendemail'

const client = new GmailOAuthClient()
await client.generateAccessToken()   // explicit token fetch via googleapis
await send({ recipient, subject, content }, client)

After — pass a plain credentials object (or nothing, falling back to GOOGLE_* env vars):

import { send } from '@weaponsforge/sendemail'

// env vars only
await send({ recipient, subject, content })

// or pass credentials explicitly
await send({ recipient, subject, content }, {
  googleUserEmail: '...',
  googleClientId: '...',
  googleClientSecret: '...',
  googleRereshToken: '...',
})

Breaking changes

  • GmailOAuthClient is no longer exported from the package public API.
  • GOOGLE_REDIRECT_URI env var is no longer required or used.
  • The accessToken pre-generation pattern (generateAccessToken()) no longer exists — Nodemailer handles token refresh internally on every send.

Test plan

  • npm run lint passes
  • npm run transpile:noemit passes (type-check)
  • npm test passes (with valid GOOGLE_* env vars in .env)
  • Send a text email via CLI: npm run sendemail:dev -- text --recipients <email> --subject "Test" --content "Hello"
  • Send an HTML email via CLI: npm run sendemail:dev -- html --recipients <email> --subject "Test" --content "Paragraph 1"

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Breaking Changes

    • GmailOAuthClient has been removed from the public API and is no longer available for import
    • send() function now accepts optional OAuth2 credentials as a parameter instead of a client instance
    • GOOGLE_REDIRECT_URI environment variable is no longer required for configuration
  • Dependencies

    • Removed googleapis from package dependencies

weaponsforge and others added 4 commits April 16, 2026 15:02
Replaces the custom GmailOAuthClient wrapper (which used googleapis to
fetch access tokens) with a plain TransportOath2SchemaType object passed
directly to Nodemailer. Nodemailer handles OAuth2 token refresh natively
via its refreshToken field, making the extra abstraction unnecessary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

This PR removes the GmailOAuthClient class and refactors OAuth2 credential handling throughout the codebase. Instead of instantiating a dedicated client class, the system now accepts optional OAuth2 credentials directly as parameters, validates them via schema, and passes them to the transport layer. Related types, schemas, and tests are removed or updated accordingly, with version bumped to 2.0.0.

Changes

Cohort / File(s) Summary
Workflow Configuration
.github/workflows/release.yml, .github/workflows/test.yml
Removed GOOGLE_REDIRECT_URI environment variable from workflow jobs; GOOGLE_REFRESH_TOKEN remains.
Documentation Updates
README.md, CLAUDE.md, docs/README_NPM.md
Removed references to GOOGLE_REDIRECT_URI environment variable and GmailOAuthClient from documented environment setup and public API listings.
Configuration Files
app/.env.example, app/package.json
Removed GOOGLE_REDIRECT_URI example entry; bumped version to 2.0.0 and removed googleapis dependency.
Type System Removal
app/src/types/oauth2client.interface.ts, app/src/types/oauth2client.schema.ts, app/src/types/oauth2client.types.ts
Deleted OAuth2 client interface, validation schemas (GmailOAuthAccessTokenSchema, GmailOAuthClientSchema), and type re-exports for OAuth2Client and GetAccessTokenResponse.
Core Module Removal
app/src/lib/google/oauth2client.ts
Removed entire GmailOAuthClient class (134 lines), including methods for token generation, access token retrieval, schema validation, and client initialization.
Public API Updates
app/src/index.ts, app/src/lib/index.ts
Removed GmailOAuthClient re-export from public module surfaces; remaining exports include send, buildHtml, EmailSender, EmailTransport, SchemaValidator.
Transport Layer Refactoring
app/src/lib/email/send.ts, app/src/lib/email/transport.ts, app/src/types/transport.interface.ts
Replaced GmailOAuthClient parameter with optional TransportOath2SchemaType in send() and createTransport3LO(); updated createTransport3LO() to validate credentials against schema and read from options with fallback to process.env.
New Schema File
app/src/types/transport.schema.ts
Added Zod schema defining TransportOath2Schema with fields googleUserEmail, googleClientId, googleClientSecret, googleRereshToken and corresponding type export.
Test Suite Updates
app/src/__tests__/oauth2client.test.ts, app/src/__tests__/send.test.ts, app/src/__tests__/sendFormat.test.ts
Removed entire oauth2client.test.ts test suite (60 lines); refactored send.test.ts and sendFormat.test.ts to remove GmailOAuthClient instantiation and use environment-based or parameter-based OAuth2 credentials instead.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through code so fine,
Removing classes, drawing a line,
OAuth2 flows with parameters light,
Schema validation makes it right! ✨
Simpler paths, no client in sight,
Version two leaps to new height! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: removing GmailOAuthClient class and the googleapis dependency, which aligns with the majority of modifications across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/SE-56

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/lib/email/send.ts (1)

12-24: ⚠️ Potential issue | 🟠 Major

Validate the email payload before creating the OAuth transport.

send() still initializes createTransport3LO() before any payload validation. With the new send(payload) usage in app/src/__tests__/sendFormat.test.ts, malformed email input now also requires valid GOOGLE_* config and can fail with transport/auth errors instead of the expected EmailSchemaMessages.*. Please short-circuit invalid params before transport setup.

Based on learnings, EmailSender class must extend EmailTransport, expose sendEmail() method, validate email params with Zod schema, and support single recipient string or recipients[] array (max 20 total).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/lib/email/send.ts` around lines 12 - 24, The send function currently
calls handler.createTransport3LO(oauth2) before validating the incoming params;
move validation to the top of send (validate EmailType with the module's Zod
schema and emit the proper EmailSchemaMessages.* on failure) so malformed
payloads short-circuit before any transport/auth is initialized. Ensure
validation accepts either a single recipient string or recipients[] and enforces
a combined maximum of 20 addresses. Update EmailSender to extend EmailTransport
and expose a sendEmail(params) method that assumes params are validated (or
re-validates internally) and uses the already-created transport; in send(),
validate first, then instantiate EmailSender, call createTransport3LO(oauth2),
and finally call EmailSender.sendEmail(validatedParams).
🧹 Nitpick comments (1)
app/src/types/transport.interface.ts (1)

17-17: Drop the semicolons here to keep the TS style consistent.

These changed members are in a TS file, and the repo style is semicolon-free.

As per coding guidelines, "No semicolons in code".

Also applies to: 30-30

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/types/transport.interface.ts` at line 17, Remove trailing semicolons
from the TypeScript interface method declarations to match the repo's
semicolon-free style: update the createTransport3LO method signature in
transport.interface.ts (and the other changed member at the same file mentioned
in the review) by removing the terminating semicolon so the declaration ends
without a semicolon; ensure all interface members in this file follow the same
no-semicolon convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/src/lib/email/send.ts`:
- Around line 12-24: The send function currently calls
handler.createTransport3LO(oauth2) before validating the incoming params; move
validation to the top of send (validate EmailType with the module's Zod schema
and emit the proper EmailSchemaMessages.* on failure) so malformed payloads
short-circuit before any transport/auth is initialized. Ensure validation
accepts either a single recipient string or recipients[] and enforces a combined
maximum of 20 addresses. Update EmailSender to extend EmailTransport and expose
a sendEmail(params) method that assumes params are validated (or re-validates
internally) and uses the already-created transport; in send(), validate first,
then instantiate EmailSender, call createTransport3LO(oauth2), and finally call
EmailSender.sendEmail(validatedParams).

---

Nitpick comments:
In `@app/src/types/transport.interface.ts`:
- Line 17: Remove trailing semicolons from the TypeScript interface method
declarations to match the repo's semicolon-free style: update the
createTransport3LO method signature in transport.interface.ts (and the other
changed member at the same file mentioned in the review) by removing the
terminating semicolon so the declaration ends without a semicolon; ensure all
interface members in this file follow the same no-semicolon convention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2e7e033-e3ce-4409-a6e8-9c40c0b4c5e2

📥 Commits

Reviewing files that changed from the base of the PR and between af42b8b and d9d22e2.

⛔ Files ignored due to path filters (1)
  • app/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • CLAUDE.md
  • README.md
  • app/.env.example
  • app/package.json
  • app/src/__tests__/oauth2client.test.ts
  • app/src/__tests__/send.test.ts
  • app/src/__tests__/sendFormat.test.ts
  • app/src/index.ts
  • app/src/lib/email/send.ts
  • app/src/lib/email/transport.ts
  • app/src/lib/google/oauth2client.ts
  • app/src/lib/index.ts
  • app/src/types/oauth2client.interface.ts
  • app/src/types/oauth2client.schema.ts
  • app/src/types/oauth2client.types.ts
  • app/src/types/transport.interface.ts
  • app/src/types/transport.schema.ts
  • docs/README_NPM.md
💤 Files with no reviewable changes (12)
  • docs/README_NPM.md
  • app/.env.example
  • README.md
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • app/src/index.ts
  • app/src/tests/oauth2client.test.ts
  • app/src/lib/index.ts
  • app/src/types/oauth2client.types.ts
  • app/src/types/oauth2client.interface.ts
  • app/src/types/oauth2client.schema.ts
  • app/src/lib/google/oauth2client.ts

@weaponsforge weaponsforge merged commit 8949a8d into dev Apr 16, 2026
4 checks passed
@weaponsforge weaponsforge deleted the refactor/SE-56 branch April 16, 2026 08:16
@weaponsforge weaponsforge mentioned this pull request Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant