1+ package io .kestra .plugin .googleworkspace .mail ;
2+
3+ import com .google .api .client .googleapis .javanet .GoogleNetHttpTransport ;
4+ import com .google .api .client .http .HttpRequest ;
5+ import com .google .api .client .http .javanet .NetHttpTransport ;
6+ import com .google .api .client .json .JsonFactory ;
7+ import com .google .api .client .json .gson .GsonFactory ;
8+ import com .google .api .services .gmail .Gmail ;
9+ import com .google .api .services .gmail .GmailScopes ;
10+ import com .google .auth .http .HttpCredentialsAdapter ;
11+ import com .google .auth .oauth2 .AccessToken ;
12+ import com .google .auth .oauth2 .GoogleCredentials ;
13+ import com .google .auth .oauth2 .UserCredentials ;
14+ import io .kestra .core .exceptions .IllegalVariableEvaluationException ;
15+ import io .kestra .core .models .property .Property ;
16+ import io .kestra .core .models .tasks .Task ;
17+ import io .kestra .core .runners .RunContext ;
18+ import io .kestra .plugin .googleworkspace .OAuthInterface ;
19+ import io .swagger .v3 .oas .annotations .media .Schema ;
20+ import jakarta .validation .constraints .NotNull ;
21+ import lombok .*;
22+ import lombok .experimental .SuperBuilder ;
23+
24+ import java .io .IOException ;
25+ import java .security .GeneralSecurityException ;
26+ import java .util .List ;
27+
28+ @ SuperBuilder
29+ @ ToString
30+ @ EqualsAndHashCode
31+ @ Getter
32+ @ NoArgsConstructor
33+ public abstract class AbstractMail extends Task implements OAuthInterface {
34+ protected static final JsonFactory JSON_FACTORY = GsonFactory .getDefaultInstance ();
35+
36+ @ Schema (
37+ title = "OAuth 2.0 Client ID" ,
38+ description = "The OAuth 2.0 client ID from Google Cloud Console"
39+ )
40+ @ NotNull
41+ protected Property <String > clientId ;
42+
43+ @ Schema (
44+ title = "OAuth 2.0 Client Secret" ,
45+ description = "The OAuth 2.0 client secret from Google Cloud Console"
46+ )
47+ @ NotNull
48+ protected Property <String > clientSecret ;
49+
50+ @ Schema (
51+ title = "OAuth 2.0 Refresh Token" ,
52+ description = "The OAuth 2.0 refresh token obtained through the authorization flow"
53+ )
54+ @ NotNull
55+ protected Property <String > refreshToken ;
56+
57+ @ Schema (
58+ title = "OAuth 2.0 Access Token" ,
59+ description = "The OAuth 2.0 access token (optional, will be generated from refresh token if not provided)"
60+ )
61+ protected Property <String > accessToken ;
62+
63+ @ Builder .Default
64+ protected Property <List <String >> scopes = Property .ofValue (List .of (
65+ GmailScopes .GMAIL_MODIFY ,
66+ GmailScopes .GMAIL_READONLY ,
67+ GmailScopes .GMAIL_SEND
68+ ));
69+
70+ @ Builder .Default
71+ protected Property <Integer > readTimeout = Property .ofValue (120 );
72+
73+ protected Gmail connection (RunContext runContext ) throws IllegalVariableEvaluationException , IOException , GeneralSecurityException {
74+ HttpCredentialsAdapter credentials = this .oauthCredentials (runContext );
75+
76+ return new Gmail .Builder (this .netHttpTransport (), JSON_FACTORY , credentials )
77+ .setApplicationName ("Kestra" )
78+ .build ();
79+ }
80+
81+ protected HttpCredentialsAdapter oauthCredentials (RunContext runContext ) throws IllegalVariableEvaluationException , IOException {
82+ // Get OAuth parameters
83+ String rClientId = runContext .render (this .clientId ).as (String .class )
84+ .orElseThrow (() -> new IllegalArgumentException ("clientId is required for OAuth authentication" ));
85+ String rClientSecret = runContext .render (this .clientSecret ).as (String .class )
86+ .orElseThrow (() -> new IllegalArgumentException ("clientSecret is required for OAuth authentication" ));
87+ String rRefreshToken = runContext .render (this .refreshToken ).as (String .class )
88+ .orElseThrow (() -> new IllegalArgumentException ("refreshToken is required for OAuth authentication" ));
89+
90+ // Optional access token
91+ String rAccessToken = runContext .render (this .accessToken ).as (String .class ).orElse (null );
92+
93+ runContext .logger ().debug ("Setting up OAuth credentials for Gmail API" );
94+
95+ // Create UserCredentials for OAuth
96+ UserCredentials .Builder credentialsBuilder = UserCredentials .newBuilder ()
97+ .setClientId (rClientId )
98+ .setClientSecret (rClientSecret )
99+ .setRefreshToken (rRefreshToken );
100+
101+ if (rAccessToken != null && !rAccessToken .trim ().isEmpty ()) {
102+ credentialsBuilder .setAccessToken (new AccessToken (rAccessToken , null ));
103+ runContext .logger ().debug ("Using provided access token for authentication" );
104+ }
105+
106+ GoogleCredentials credentials = credentialsBuilder .build ();
107+
108+ // Apply scopes if specified
109+ var rScopes = runContext .render (this .scopes ).asList (String .class );
110+ if (rScopes != null && !rScopes .isEmpty ()) {
111+ credentials = credentials .createScoped (rScopes );
112+ runContext .logger ().debug ("Applied {} OAuth scopes" , rScopes .size ());
113+ }
114+
115+ var rTimeout = runContext .render (this .readTimeout ).as (Integer .class ).orElse (120 );
116+ return new HttpCredentialsAdapter (credentials ) {
117+ @ Override
118+ public void initialize (HttpRequest request ) throws IOException {
119+ super .initialize (request );
120+ request .setReadTimeout (rTimeout * 1000 );
121+ }
122+ };
123+ }
124+
125+ protected NetHttpTransport netHttpTransport () throws GeneralSecurityException , IOException {
126+ return GoogleNetHttpTransport .newTrustedTransport ();
127+ }
128+ }
0 commit comments