From 2cd15509ba77b619b39403d1234d6a16d1aa7044 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sat, 25 Apr 2026 17:56:37 +0200 Subject: [PATCH] feat: add email connector and sign-in experience methods to LogtoManagementClient Co-Authored-By: Claude Sonnet 4.6 --- .../saas/identity/LogtoManagementClient.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/main/java/net/siegeln/cameleer/saas/identity/LogtoManagementClient.java b/src/main/java/net/siegeln/cameleer/saas/identity/LogtoManagementClient.java index 96465e7..8defdc2 100644 --- a/src/main/java/net/siegeln/cameleer/saas/identity/LogtoManagementClient.java +++ b/src/main/java/net/siegeln/cameleer/saas/identity/LogtoManagementClient.java @@ -398,6 +398,122 @@ public class LogtoManagementClient { .toBodilessEntity(); } + // --- Email Connector Management --- + + /** List all connector factories available in Logto. */ + @SuppressWarnings("unchecked") + public List> listConnectorFactories() { + if (!isAvailable()) return List.of(); + try { + var resp = restClient.get() + .uri(config.getLogtoEndpoint() + "/api/connector-factories") + .header("Authorization", "Bearer " + getAccessToken()) + .retrieve() + .body(List.class); + return resp != null ? resp : List.of(); + } catch (Exception e) { + log.warn("Failed to list connector factories: {}", e.getMessage()); + return List.of(); + } + } + + /** List all connectors. */ + @SuppressWarnings("unchecked") + public List> listConnectors() { + if (!isAvailable()) return List.of(); + try { + var resp = restClient.get() + .uri(config.getLogtoEndpoint() + "/api/connectors") + .header("Authorization", "Bearer " + getAccessToken()) + .retrieve() + .body(List.class); + return resp != null ? resp : List.of(); + } catch (Exception e) { + log.warn("Failed to list connectors: {}", e.getMessage()); + return List.of(); + } + } + + /** Create a connector from a factory. */ + @SuppressWarnings("unchecked") + public Map createConnector(String factoryId, Map connectorConfig) { + if (!isAvailable()) return null; + var body = new java.util.HashMap(); + body.put("connectorId", factoryId); + body.put("config", connectorConfig); + return (Map) restClient.post() + .uri(config.getLogtoEndpoint() + "/api/connectors") + .header("Authorization", "Bearer " + getAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .body(body) + .retrieve() + .body(Map.class); + } + + /** Update an existing connector's config. */ + @SuppressWarnings("unchecked") + public Map updateConnector(String connectorId, Map connectorConfig) { + if (!isAvailable()) return null; + return (Map) restClient.patch() + .uri(config.getLogtoEndpoint() + "/api/connectors/" + connectorId) + .header("Authorization", "Bearer " + getAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of("config", connectorConfig)) + .retrieve() + .body(Map.class); + } + + /** Delete a connector. */ + public void deleteConnector(String connectorId) { + if (!isAvailable()) return; + restClient.delete() + .uri(config.getLogtoEndpoint() + "/api/connectors/" + connectorId) + .header("Authorization", "Bearer " + getAccessToken()) + .retrieve() + .toBodilessEntity(); + } + + /** Test a connector by sending a real email. Uses Logto's built-in test endpoint. */ + public void testConnector(String factoryId, String email, Map connectorConfig) { + if (!isAvailable()) throw new IllegalStateException("Logto not configured"); + restClient.post() + .uri(config.getLogtoEndpoint() + "/api/connectors/" + factoryId + "/test") + .header("Authorization", "Bearer " + getAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of("email", email, "config", connectorConfig)) + .retrieve() + .toBodilessEntity(); + } + + /** Get the current sign-in experience config. */ + @SuppressWarnings("unchecked") + public Map getSignInExperience() { + if (!isAvailable()) return null; + try { + return (Map) restClient.get() + .uri(config.getLogtoEndpoint() + "/api/sign-in-exp") + .header("Authorization", "Bearer " + getAccessToken()) + .retrieve() + .body(Map.class); + } catch (Exception e) { + log.warn("Failed to get sign-in experience: {}", e.getMessage()); + return null; + } + } + + /** Update the sign-in experience config (partial update). */ + @SuppressWarnings("unchecked") + public Map updateSignInExperience(Map updates) { + if (!isAvailable()) return null; + return (Map) restClient.patch() + .uri(config.getLogtoEndpoint() + "/api/sign-in-exp") + .header("Authorization", "Bearer " + getAccessToken()) + .contentType(MediaType.APPLICATION_JSON) + .body(updates) + .retrieve() + .body(Map.class); + } + /** Update a user's password. */ public void updateUserPassword(String userId, String newPassword) { if (!isAvailable()) throw new IllegalStateException("Logto not configured");