Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"permissions": {
"allow": [
"Bash(export JAVA_HOME=/Users/gordan.o@nylas.com/Library/Java/JavaVirtualMachines/corretto-11.0.29/Contents/Home:*)",
"Bash(./gradlew test:*)",
"Bash(export JAVA_HOME:*)",
"Bash(./gradlew jacocoTestReport:*)",
Expand All @@ -10,7 +9,8 @@
"Bash(find:*)",
"Bash(./gradlew clean build:*)",
"Bash(java -version:*)",
"Bash(/usr/libexec/java_home:*)"
"Bash(/usr/libexec/java_home:*)",
"Bash(./gradlew clean test:*)"
]
}
}
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Nylas Java SDK Changelog

## [Unreleased]

### Added
* Multi-credential authentication support allowing multiple provider credentials per Connector
- `CreateCredentialRequest.Connector` class for creating connector credentials with `client_id`, `client_secret`, and optional extra properties like `tenant`
- `credentialId` field in `UrlForAuthenticationConfig` for hosted auth URL generation via `urlForOAuth2` and `urlForOAuth2PKCE`
- `credentialId` field in `CreateGrantRequest` for custom authentication
- `credentialId` field in `Grant` response model
- `activeCredentialId` field in `Connector` response model
- `activeCredentialId` field in `UpdateConnectorRequest` for setting the active credential on a Connector
* Enhanced `CredentialData.ConnectorOverride` to support optional `clientId` and `clientSecret` fields

### Deprecated
* `CreateCredentialRequest.Override` - Use `CreateCredentialRequest.Connector` instead

## [2.14.1]

### Added
Expand Down
19 changes: 17 additions & 2 deletions src/main/kotlin/com/nylas/models/Connector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ sealed class Connector(
*/
@Json(name = "provider")
val provider: AuthProvider,
/**
* The ID of the active credential for this connector (for multi-credential setups).
*/
@Json(name = "active_credential_id")
open val activeCredentialId: String? = null,
) {
/**
* Class representing a Google connector creation request.
Expand All @@ -27,7 +32,12 @@ sealed class Connector(
*/
@Json(name = "scope")
val scope: List<String>? = null,
) : Connector(AuthProvider.GOOGLE)
/**
* The ID of the active credential for this connector
*/
@Json(name = "active_credential_id")
override val activeCredentialId: String? = null,
) : Connector(AuthProvider.GOOGLE, activeCredentialId)

/**
* Class representing a Microsoft connector creation request.
Expand All @@ -43,7 +53,12 @@ sealed class Connector(
*/
@Json(name = "scope")
val scope: List<String>? = null,
) : Connector(AuthProvider.MICROSOFT)
/**
* The ID of the active credential for this connector
*/
@Json(name = "active_credential_id")
override val activeCredentialId: String? = null,
) : Connector(AuthProvider.MICROSOFT, activeCredentialId)

/**
* Class representing an IMAP connector creation request.
Expand Down
24 changes: 22 additions & 2 deletions src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,28 @@ sealed class CreateCredentialRequest(
) : CreateCredentialRequest(name, credentialData, CredentialType.SERVICEACCOUNT)

/**
* Class representing a request to create an override credential
* Class representing a request to create a connector credential.
* For multi-credential OAuth flows, provide clientId and clientSecret in credentialData.
* For other overrides, use extraProperties in credentialData.
*/
data class Connector(
/**
* Unique name of this credential
*/
@Json(name = "name")
override val name: String,
/**
* Data that specifies the credential details (client_id/client_secret for OAuth, or extraProperties for overrides)
*/
@Json(name = "credential_data")
override val credentialData: CredentialData.ConnectorOverride,
) : CreateCredentialRequest(name, credentialData, CredentialType.CONNECTOR)

/**
* Alias for [Connector] to maintain backward compatibility.
* @deprecated Use [Connector] instead.
*/
@Deprecated("Use Connector instead", ReplaceWith("Connector"))
data class Override(
/**
* Unique name of this credential
Expand All @@ -77,6 +97,6 @@ sealed class CreateCredentialRequest(
PolymorphicJsonAdapterFactory.of(CreateCredentialRequest::class.java, "credential_type")
.withSubtype(Microsoft::class.java, CredentialType.ADMINCONSENT.value)
.withSubtype(Google::class.java, CredentialType.SERVICEACCOUNT.value)
.withSubtype(Override::class.java, CredentialType.CONNECTOR.value)
.withSubtype(Connector::class.java, CredentialType.CONNECTOR.value)
}
}
15 changes: 14 additions & 1 deletion src/main/kotlin/com/nylas/models/CreateGrantRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ data class CreateGrantRequest(
*/
@Json(name = "scope")
val scope: List<String>? = null,
/**
* The credential ID to use for authentication (for multi-credential setups).
*/
@Json(name = "credential_id")
val credentialId: String? = null,
) {
/**
* Builder for [CreateGrantRequest].
Expand All @@ -38,6 +43,7 @@ data class CreateGrantRequest(
) {
private var state: String? = null
private var scopes: List<String>? = null
private var credentialId: String? = null

/**
* Set the state value to return to developer's website after authentication flow is completed.
Expand All @@ -53,10 +59,17 @@ data class CreateGrantRequest(
*/
fun scopes(scopes: List<String>) = apply { this.scopes = scopes }

/**
* Set the credential ID to use for authentication (for multi-credential setups).
* @param credentialId The credential ID
* @return This builder
*/
fun credentialId(credentialId: String) = apply { this.credentialId = credentialId }

/**
* Build the [CreateGrantRequest].
* @return The built [CreateGrantRequest]
*/
fun build() = CreateGrantRequest(provider, settings, state, scopes)
fun build() = CreateGrantRequest(provider, settings, state, scopes, credentialId)
}
}
10 changes: 8 additions & 2 deletions src/main/kotlin/com/nylas/models/CredentialData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ sealed class CredentialData(
) : CredentialData(extraProperties)

/**
* Class representing additional data needed to create a credential for a Connector Override
* Class representing additional data needed to create a credential for a Connector Override.
* For multi-credential OAuth flows, provide clientId and clientSecret.
* For other overrides, use extraProperties.
*/
data class ConnectorOverride(
override val extraProperties: Map<String, String>,
@Json(name = "client_id")
val clientId: String? = null,
@Json(name = "client_secret")
val clientSecret: String? = null,
override val extraProperties: Map<String, String>? = emptyMap(),
) : CredentialData(extraProperties)
}
5 changes: 5 additions & 0 deletions src/main/kotlin/com/nylas/models/Grant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ data class Grant(
*/
@Json(name = "settings")
val settings: Map<String, Any>? = null,
/**
* The credential ID associated with this grant (for multi-credential setups).
*/
@Json(name = "credential_id")
val credentialId: String? = null,
)
30 changes: 28 additions & 2 deletions src/main/kotlin/com/nylas/models/UpdateConnectorRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ sealed class UpdateConnectorRequest {
*/
@Json(name = "scope")
val scope: List<String>? = null,
/**
* The ID of the active credential for this connector (for multi-credential setups)
*/
@Json(name = "active_credential_id")
val activeCredentialId: String? = null,
) : UpdateConnectorRequest() {
class Builder {
private var settings: GoogleConnectorSettings? = null
private var scope: List<String>? = null
private var activeCredentialId: String? = null

/**
* Set the Google OAuth provider credentials and settings
Expand All @@ -39,11 +45,18 @@ sealed class UpdateConnectorRequest {
*/
fun scope(scope: List<String>) = apply { this.scope = scope }

/**
* Set the active credential ID for this connector
* @param activeCredentialId The active credential ID
* @return The builder
*/
fun activeCredentialId(activeCredentialId: String) = apply { this.activeCredentialId = activeCredentialId }

/**
* Build the Google connector creation request
* @return The Google connector creation request
*/
fun build() = Google(settings, scope)
fun build() = Google(settings, scope, activeCredentialId)
}
}

Expand All @@ -61,10 +74,16 @@ sealed class UpdateConnectorRequest {
*/
@Json(name = "scope")
val scope: List<String>? = null,
/**
* The ID of the active credential for this connector (for multi-credential setups)
*/
@Json(name = "active_credential_id")
val activeCredentialId: String? = null,
) : UpdateConnectorRequest() {
class Builder {
private var settings: MicrosoftConnectorSettings? = null
private var scope: List<String>? = null
private var activeCredentialId: String? = null

/**
* Set the Microsoft OAuth provider credentials and settings
Expand All @@ -80,11 +99,18 @@ sealed class UpdateConnectorRequest {
*/
fun scope(scope: List<String>) = apply { this.scope = scope }

/**
* Set the active credential ID for this connector
* @param activeCredentialId The active credential ID
* @return The builder
*/
fun activeCredentialId(activeCredentialId: String) = apply { this.activeCredentialId = activeCredentialId }

/**
* Build the Microsoft connector creation request
* @return The Microsoft connector creation request
*/
fun build() = Microsoft(settings, scope)
fun build() = Microsoft(settings, scope, activeCredentialId)
}
}
}
15 changes: 15 additions & 0 deletions src/main/kotlin/com/nylas/models/UrlForAuthenticationConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ data class UrlForAuthenticationConfig(
*/
@Json(name = "login_hint")
val loginHint: String? = null,
/**
* The credential ID to use for authentication (for multi-credential setups).
* Allowed when response_type is "code".
*/
@Json(name = "credential_id")
val credentialId: String? = null,
) {
/**
* Builder for [UrlForAuthenticationConfig].
Expand All @@ -71,6 +77,7 @@ data class UrlForAuthenticationConfig(
private var includeGrantScopes: Boolean? = null
private var state: String? = null
private var loginHint: String? = null
private var credentialId: String? = null

/**
* Set the integration provider type that you already had set up with Nylas for this application.
Expand Down Expand Up @@ -124,6 +131,13 @@ data class UrlForAuthenticationConfig(
*/
fun loginHint(loginHint: String) = apply { this.loginHint = loginHint }

/**
* Set the credential ID to use for authentication (for multi-credential setups).
* @param credentialId The credential ID.
* @return This builder.
*/
fun credentialId(credentialId: String) = apply { this.credentialId = credentialId }

/**
* Build the [UrlForAuthenticationConfig].
* @return The [UrlForAuthenticationConfig].
Expand All @@ -138,6 +152,7 @@ data class UrlForAuthenticationConfig(
includeGrantScopes,
state,
loginHint,
credentialId,
)
}
}
14 changes: 10 additions & 4 deletions src/main/kotlin/com/nylas/util/CredentialDataAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -122,22 +122,28 @@ class GoogleServiceAccountCredentialDataAdapter {
class ConnectorOverrideCredentialDataAdapter {
@FromJson
fun fromJson(reader: JsonReader): CredentialData.ConnectorOverride {
var clientId: String? = null
var clientSecret: String? = null
val extraProperties = mutableMapOf<String, String>()

reader.beginObject()
while (reader.hasNext()) {
val key = reader.nextName()
val value = reader.nextString()
extraProperties[key] = value
when (val key = reader.nextName()) {
"client_id" -> clientId = reader.nextString()
"client_secret" -> clientSecret = reader.nextString()
else -> extraProperties[key] = reader.nextString()
}
}
reader.endObject()

return CredentialData.ConnectorOverride(extraProperties)
return CredentialData.ConnectorOverride(clientId, clientSecret, extraProperties)
}

@ToJson
fun toJson(writer: JsonWriter, value: CredentialData.ConnectorOverride?) {
writer.beginObject()
value?.clientId?.let { writer.name("client_id").value(it) }
value?.clientSecret?.let { writer.name("client_secret").value(it) }
value?.extraProperties?.forEach { (k, v) ->
writer.name(k).value(v)
}
Expand Down
Loading
Loading