A Spring Boot / Kotlin application that serves as an HTTP-to-Pulsar bridge and Microsoft Graph webhook processor.
flowchart TB
subgraph External["External Sources"]
MSGraph["Microsoft Graph API"]
Client["HTTP Client"]
end
subgraph App["Spring Boot Application (Port 8888)"]
subgraph Controllers["Controllers"]
GNC["GraphNotificationsController<br/>/webhook/notifications<br/>/webhook/lifecycleNotifications"]
MC["MessageController<br/>/api/messages"]
end
subgraph Services["Services"]
GNP["GraphNotificationProcessor"]
MP["MessageProducer"]
end
subgraph Helpers["Helpers"]
GCH["GraphClientHelper"]
end
subgraph Config["Configuration"]
PC["PulsarConfig"]
GOM["GraphObjectMapper"]
end
end
subgraph Downstream["Downstream Systems"]
Pulsar["Apache Pulsar<br/>Topic: reply.text_classifier.message"]
GraphAPI["MS Graph API<br/>(Fetch Mail & Attachments)"]
end
%% Direct Message Flow
Client -->|"POST /api/messages"| MC
MC --> MP
MP --> Pulsar
PC -.->|"configures"| MP
%% Graph Webhook Flow
MSGraph -->|"Webhook POST"| GNC
GNC -->|"Validation Request"| MSGraph
GNC --> GNP
GNP -->|"ChangeType.Created"| GCH
GCH --> GraphAPI
GOM -.->|"deserializes"| GNC
%% Styling
classDef controller fill:#e1f5fe,stroke:#01579b
classDef service fill:#e8f5e9,stroke:#2e7d32
classDef config fill:#fff3e0,stroke:#e65100
classDef external fill:#fce4ec,stroke:#880e4f
classDef downstream fill:#f3e5f5,stroke:#4a148c
class GNC,MC controller
class GNP,MP service
class PC,GOM,GCH config
class MSGraph,Client external
class Pulsar,GraphAPI downstream
sequenceDiagram
participant Client
participant MessageController
participant MessageProducer
participant Pulsar
Client->>MessageController: POST /api/messages
MessageController->>MessageProducer: send(message)
MessageProducer->>Pulsar: publish to topic
Pulsar-->>MessageProducer: ack
MessageProducer-->>MessageController: success
MessageController-->>Client: 200 OK
sequenceDiagram
participant MSGraph as Microsoft Graph
participant GNC as GraphNotificationsController
participant GNP as GraphNotificationProcessor
participant GCH as GraphClientHelper
participant GraphAPI as MS Graph API
Note over MSGraph,GNC: Subscription Validation
MSGraph->>GNC: POST /webhook/notifications?validationToken=xxx
GNC-->>MSGraph: 200 OK (validationToken)
Note over MSGraph,GraphAPI: Notification Processing
MSGraph->>GNC: POST /webhook/notifications (ChangeNotificationCollection)
GNC->>GNP: process(collection)
loop For each notification
GNP->>GNP: processNotification()
alt ChangeType == Created
GNP->>GCH: getGraphClient()
GCH-->>GNP: GraphServiceClient
GNP->>GraphAPI: GET /users/{id}/messages/{id}
GraphAPI-->>GNP: Message content
GNP->>GraphAPI: GET /users/{id}/messages/{id}/attachments
GraphAPI-->>GNP: Attachments list
end
end
GNC-->>MSGraph: 202 Accepted
| Endpoint | Method | Description |
|---|---|---|
/api/messages |
POST | Send a message directly to Pulsar topic |
/webhook/notifications |
POST | Receive MS Graph change notifications |
/webhook/lifecycleNotifications |
POST | Receive MS Graph lifecycle notifications |
| Property | Description | Default |
|---|---|---|
pulsar.service-url |
Pulsar broker URL | pulsar://localhost:6650 |
pulsar.topic |
Target Pulsar topic | persistent://public/default/reply.text_classifier.message |
server.port |
HTTP server port | 8888 |
graph.client-id |
Azure AD client ID | - |
graph.client-secret |
Azure AD client secret | - |
graph.tenant-id |
Azure AD tenant ID | - |
# Build the project
./gradlew build
# Run the application
./gradlew bootRun
# Run tests
./gradlew test- Kotlin with Java 24 toolchain
- Spring Boot 4.0.1 with Spring for Apache Pulsar
- Microsoft Graph SDK for Java
- Azure Identity for OAuth2 authentication
- Apache Pulsar for message streaming
- Gradle build system