Skip to content

Commit 3221bf5

Browse files
nafgclaude
andcommitted
docs: add comprehensive documentation and development guides
- Add CLAUDE.md with build commands, architecture overview, and development patterns - Add AGENTS.md with repository guidelines and coding conventions - Enhance README.md with complete usage examples for Twilio integration - Add zio-messaging-entrance-group/README.md with Entrance Group-specific documentation - Move Entrance Group examples to subdirectory to keep main README focused on Twilio 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent f09a225 commit 3221bf5

File tree

4 files changed

+358
-1
lines changed

4 files changed

+358
-1
lines changed

AGENTS.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- `bleep.yaml` defines a multi-module build with shared templates and cross-builds (Scala 2.13/3).
5+
- Modules live at the repo root: `zio-messaging` (core API), `zio-messaging-twilio` (Twilio integration), and `zio-messaging-entrance-group` (Entrance Group integration).
6+
- Source code follows the bleep “cross-pure” layout under `*/src/scala/...` (for example, `zio-messaging/src/scala/io/github/nafg/messaging`).
7+
- There are currently no test sources; if you add tests, place them under `*/src/test/scala/...` for the relevant module.
8+
9+
## Build, Test, and Development Commands
10+
- `bleep projects` — list available projects/modules.
11+
- `bleep compile <project>` — compile a module, e.g. `bleep compile zio-messaging`.
12+
- `bleep test <project>` — run tests for a module (no tests yet, but this is the standard entry point once added).
13+
- These modules are libraries (no runnable app entry point in this repo), so there is no default `run` target.
14+
15+
## Coding Style & Naming Conventions
16+
- Formatting is enforced by Scalafmt (`.scalafmt.conf`): IntelliJ preset, `maxColumn = 120`, `scala213source3` dialect.
17+
- Prefer letting Scalafmt handle indentation/alignment instead of manual formatting.
18+
- Follow existing package structure (`io.github.nafg.messaging.*`), PascalCase for types, camelCase for methods/vals.
19+
20+
## Testing Guidelines
21+
- If adding tests, add the test framework dependency in `bleep.yaml` and place tests in the module’s `src/test/scala` tree.
22+
- Name specs descriptively (e.g., `TwilioSmsServiceSpec`) and keep tests close to the corresponding module.
23+
24+
## Commit & Pull Request Guidelines
25+
- Commit messages follow Conventional Commits with optional scope, e.g. `refactor(api): ...`, `chore(deps): ...`, `ci: ...`.
26+
- Prefer small, focused PRs with: a clear summary, testing notes (or “not applicable”), and linked issues when relevant.
27+
- If you update public APIs, also update documentation or usage examples where applicable.
28+
29+
## Release & Publishing Notes
30+
- Publishing metadata lives in `bleep.publish.yaml` and uses the `scripts` project configured in `bleep.yaml`.
31+
- Tags follow a `vX.Y.Z` pattern (for example, `v0.3.0`), so align release notes and versioning accordingly.

CLAUDE.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build System
6+
7+
This project uses **Bleep** as its build tool.
8+
9+
### Common Commands
10+
11+
- **List all projects**: `bleep projects`
12+
- **Compile all modules**: `bleep compile`
13+
- **Compile specific module**: `bleep compile <project-name>` (e.g., `bleep compile zio-messaging`)
14+
- **Run tests**: `bleep test <project-name>` (once tests are added)
15+
- **Publish**: `bleep publish -- --mode=portal-api:AUTOMATIC` (requires credentials in env vars)
16+
17+
### Important Build Notes
18+
19+
- The build uses `source-layout: cross-pure` with sources under `*/src/scala/...`
20+
- All modules cross-build for Scala 2.13 and Scala 3 using the `template-cross-all` template
21+
- JVM target: GraalVM Java 17 (22.3.1)
22+
23+
## Project Architecture
24+
25+
This is a **multi-module library** for ZIO-based messaging integrations:
26+
27+
### Module Structure
28+
29+
1. **zio-messaging** (core module)
30+
- Defines the `SmsService` trait with `sendMessage(to: Seq[PhoneNumber], message: String): Task[Unit]`
31+
- Provides `SmsService.NoOp` as a no-op implementation
32+
- Uses `io.github.nafg.scalaphonenumber` for phone number handling
33+
- No external messaging dependencies
34+
35+
2. **zio-messaging-twilio**
36+
- Depends on: `zio-messaging`, Twilio SDK
37+
- Implements `TwilioSmsService` that wraps Twilio's REST API
38+
- Requires `TwilioClient` and `TwilioSmsService.Config(from: PhoneNumber)`
39+
- Provides ZLayer for dependency injection
40+
41+
3. **zio-messaging-entrance-group**
42+
- Depends on: `zio-messaging`, zio-http
43+
- Two implementations:
44+
- `EntranceGroupHookCampaignSmsService`: uses existing campaign via webhook
45+
- `EntranceGroupCreateManualCampaignSmsService`: creates new campaigns per message
46+
- `EntranceGroupApi` defines typed endpoints using zio-http's endpoint DSL
47+
- Uses Schema-based serialization for request/response bodies
48+
49+
### Design Patterns
50+
51+
- All implementations extend the common `SmsService` trait
52+
- ZLayer-based dependency injection (e.g., `TwilioSmsService.layer`, `EntranceGroupHookCampaignSmsService.layer`)
53+
- Config case classes for service configuration (e.g., `Config(from: PhoneNumber)`, `Config(campaignId: Long, authKey: Secret, authValue: Secret)`)
54+
- Phone numbers typed using `PhoneNumber` from scala-phonenumber library (E.164 format)
55+
- ZIO effects for error handling and async operations
56+
57+
## Code Formatting
58+
59+
- Scalafmt is configured (`.scalafmt.conf`)
60+
- Uses IntelliJ preset with `maxColumn = 120`
61+
- Dialect: `scala213source3` for Scala 2.13 with `-Xsource:3` compatibility
62+
- Always format code before committing
63+
64+
## Package Structure
65+
66+
- Base package: `io.github.nafg.messaging`
67+
- Twilio: `io.github.nafg.messaging.twilio`
68+
- Entrance Group: `io.github.nafg.messaging.entrancegrp`
69+
70+
## Publishing
71+
72+
- Publishing metadata in `bleep.publish.yaml`
73+
- Group ID: `io.github.nafg.zio-messaging`
74+
- All three modules are published to Sonatype
75+
- Tags follow `vX.Y.Z` pattern (e.g., `v0.3.0`)
76+
77+
## CI/CD
78+
79+
- GitHub Actions workflow in `.github/workflows/ci.yml`
80+
- Runs on: push to main, PRs, tag pushes, merge groups
81+
- Build job: compiles all modules with `bleep compile`
82+
- Publish job: automatically publishes on version tags (`v*`) to Sonatype using portal API

README.md

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,126 @@
1-
# zio-sms
1+
# zio-messaging
2+
3+
[![CI](https://github.com/io-github-nafg/zio-messaging/actions/workflows/ci.yml/badge.svg)](https://github.com/io-github-nafg/zio-messaging/actions/workflows/ci.yml)
4+
5+
ZIO wrappers for messaging APIs, providing type-safe, functional interfaces for sending SMS messages through various providers.
6+
7+
## Features
8+
9+
- **Type-safe**: Uses `PhoneNumber` from [scala-phonenumber](https://github.com/nafg/scala-phonenumber) for validated phone numbers
10+
- **Functional**: Built on ZIO for composable, testable, and error-safe code
11+
- **Modular**: Choose the integration you need (Twilio, Entrance Group, or implement your own)
12+
- **Cross-compiled**: Supports both Scala 2.13 and Scala 3
13+
14+
## Modules
15+
16+
### `zio-messaging` (Core)
17+
18+
The core module defines the `SmsService` trait that all implementations extend:
19+
20+
```scala
21+
trait SmsService {
22+
def sendMessage(to: Seq[PhoneNumber], message: String): Task[Unit]
23+
}
24+
```
25+
26+
Also includes a no-op implementation that doesn't send messages (useful for testing, development, or disabling SMS):
27+
28+
```scala
29+
SmsService.NoOp.layer
30+
```
31+
32+
### `zio-messaging-twilio`
33+
34+
Twilio SMS integration using the official Twilio SDK.
35+
36+
### `zio-messaging-entrance-group`
37+
38+
Entrance Group SMS integration with two implementation options:
39+
- **Hook Campaign**: Use an existing campaign via webhook
40+
- **Manual Campaign**: Create new campaigns per message
41+
42+
See [zio-messaging-entrance-group/README.md](zio-messaging-entrance-group/README.md) for usage examples and details.
43+
44+
## Installation
45+
46+
Add the following to your `build.sbt`:
47+
48+
```scala
49+
libraryDependencies += "io.github.nafg.zio-messaging" %% "zio-messaging-twilio" % "<version>"
50+
```
51+
52+
For Bleep (`bleep.yaml`):
53+
54+
```yaml
55+
dependencies:
56+
- io.github.nafg.zio-messaging::zio-messaging-twilio:<version>
57+
```
58+
59+
## Usage
60+
61+
### Twilio Example
62+
63+
```scala
64+
import io.github.nafg.messaging.SmsService
65+
import io.github.nafg.messaging.twilio.{TwilioClient, TwilioSmsService}
66+
import io.github.nafg.scalaphonenumber.PhoneNumber
67+
import zio._
68+
69+
val twilioClientLayer = ZLayer.succeed(
70+
TwilioClient.Config(
71+
accountSid = "your-account-sid",
72+
authToken = "your-auth-token"
73+
)
74+
) >>> TwilioClient.layer
75+
76+
val twilioSmsLayer = ZLayer.succeed(
77+
TwilioSmsService.Config(
78+
from = PhoneNumber.parseInternational("+15551234567").get
79+
)
80+
) ++ twilioClientLayer >>> TwilioSmsService.layer
81+
82+
val program = for {
83+
sms <- ZIO.service[SmsService]
84+
recipient = PhoneNumber.parseInternational("+15559876543").get
85+
_ <- sms.sendMessage(Seq(recipient), "Hello from ZIO!")
86+
} yield ()
87+
88+
program.provide(twilioSmsLayer)
89+
```
90+
91+
## No-Op Implementation
92+
93+
When you don't want to send actual messages (e.g., testing, local development, or feature toggling), use the no-op implementation:
94+
95+
```scala
96+
import io.github.nafg.messaging.SmsService
97+
98+
val program = for {
99+
sms <- ZIO.service[SmsService]
100+
_ <- sms.sendMessage(recipients, "This won't actually send")
101+
} yield ()
102+
103+
program.provide(SmsService.NoOp.layer)
104+
```
105+
106+
## Phone Number Handling
107+
108+
All phone numbers use the `PhoneNumber` type from [scala-phonenumber](https://github.com/nafg/scala-phonenumber):
109+
110+
```scala
111+
import io.github.nafg.scalaphonenumber.PhoneNumber
112+
113+
// Parse international format
114+
val number = PhoneNumber.parseInternational("+15551234567")
115+
116+
// Format as E.164
117+
number.map(_.formatE164) // "+15551234567"
118+
```
119+
120+
## License
121+
122+
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details.
123+
124+
## Contributing
125+
126+
Contributions are welcome! Please feel free to submit a Pull Request.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# zio-messaging-entrance-group
2+
3+
Entrance Group SMS integration for ZIO applications.
4+
5+
## Overview
6+
7+
This module provides two implementations for sending SMS messages through Entrance Group:
8+
9+
1. **Hook Campaign** (`EntranceGroupHookCampaignSmsService`): Uses an existing campaign via webhook
10+
2. **Manual Campaign** (`EntranceGroupCreateManualCampaignSmsService`): Creates new campaigns per message
11+
12+
## Installation
13+
14+
Add to your `build.sbt`:
15+
16+
```scala
17+
libraryDependencies += "io.github.nafg.zio-messaging" %% "zio-messaging-entrance-group" % "<version>"
18+
```
19+
20+
For Bleep (`bleep.yaml`):
21+
22+
```yaml
23+
dependencies:
24+
- io.github.nafg.zio-messaging::zio-messaging-entrance-group:<version>
25+
```
26+
27+
## Usage
28+
29+
### Hook Campaign Example
30+
31+
Use this when you have an existing campaign and want to send messages via its webhook:
32+
33+
```scala
34+
import io.github.nafg.messaging.SmsService
35+
import io.github.nafg.messaging.entrancegrp.EntranceGroupHookCampaignSmsService
36+
import io.github.nafg.scalaphonenumber.PhoneNumber
37+
import zio._
38+
import zio.http.Client
39+
40+
val entranceGroupLayer = ZLayer.succeed(
41+
EntranceGroupHookCampaignSmsService.Config(
42+
campaignId = 12345L,
43+
authKey = Config.Secret("X-API-Key"),
44+
authValue = Config.Secret("your-api-key")
45+
)
46+
) ++ Client.default >>> EntranceGroupHookCampaignSmsService.layer
47+
48+
val program = for {
49+
sms <- ZIO.service[SmsService]
50+
recipient = PhoneNumber.parseInternational("+15559876543").get
51+
_ <- sms.sendMessage(Seq(recipient), "Hello from Entrance Group!")
52+
} yield ()
53+
54+
program.provide(entranceGroupLayer)
55+
```
56+
57+
### Manual Campaign Example
58+
59+
Use this when you want to create a new campaign for each batch of messages:
60+
61+
```scala
62+
import io.github.nafg.messaging.SmsService
63+
import io.github.nafg.messaging.entrancegrp.EntranceGroupCreateManualCampaignSmsService
64+
import io.github.nafg.scalaphonenumber.PhoneNumber
65+
import zio._
66+
import zio.http.Client
67+
68+
val entranceGroupLayer = ZLayer.succeed(
69+
EntranceGroupCreateManualCampaignSmsService.Config(
70+
campaignName = "My Campaign",
71+
authKey = Config.Secret("X-API-Key"),
72+
authValue = Config.Secret("your-api-key")
73+
)
74+
) ++ Client.default >>> EntranceGroupCreateManualCampaignSmsService.layer
75+
76+
val program = for {
77+
sms <- ZIO.service[SmsService]
78+
recipients = Seq(
79+
PhoneNumber.parseInternational("+15551111111").get,
80+
PhoneNumber.parseInternational("+15552222222").get
81+
)
82+
_ <- sms.sendMessage(recipients, "Broadcast message!")
83+
} yield ()
84+
85+
program.provide(entranceGroupLayer)
86+
```
87+
88+
## Configuration
89+
90+
### EntranceGroupHookCampaignSmsService.Config
91+
92+
- `campaignId: Long` - The ID of the existing campaign
93+
- `authKey: Config.Secret` - The authentication header key (e.g., "X-API-Key")
94+
- `authValue: Config.Secret` - The authentication header value (your API key)
95+
96+
### EntranceGroupCreateManualCampaignSmsService.Config
97+
98+
- `campaignName: String` - The name for the campaigns that will be created
99+
- `authKey: Config.Secret` - The authentication header key (e.g., "X-API-Key")
100+
- `authValue: Config.Secret` - The authentication header value (your API key)
101+
102+
## API Endpoints
103+
104+
This module uses two Entrance Group API endpoints:
105+
106+
- **Hook Campaign**: `POST https://entrancegrp.com/api/hooks/campaigns/{id}`
107+
- **Manual Campaign**: `POST https://apiv2.entrancegrp.com/campaigns`
108+
109+
All endpoints are defined in `EntranceGroupApi` using zio-http's type-safe endpoint DSL.
110+
111+
## Dependencies
112+
113+
- `zio-messaging` (core module)
114+
- `zio-http` for HTTP client and endpoint definitions
115+
- `scala-phonenumber` for phone number handling
116+
117+
## License
118+
119+
Licensed under the Apache License, Version 2.0. See the main [LICENSE](../LICENSE) file for details.

0 commit comments

Comments
 (0)