-
Notifications
You must be signed in to change notification settings - Fork 1
SOLAPI Kotlin SDK 1.1.0 #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Palbahngmiyine
wants to merge
43
commits into
solapi:main
Choose a base branch
from
Palbahngmiyine:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
1d933f5
docs: expand CLAUDE.md with package structure, commands, and version …
Palbahngmiyine 8eb0072
chore: upgrade Gradle 9.3.0, Kotlin 2.3.0, and dependencies
Palbahngmiyine 4befdba
docs: add Tidy First principles to CLAUDE.md
Palbahngmiyine 207d272
refactor: migrate from java.time to kotlin.time for date/time handling
Palbahngmiyine 618f129
test: add unit tests for kotlin.time migration
Palbahngmiyine 9a1498f
docs: add AGENTS.md knowledge base for SDK navigation and patterns
Palbahngmiyine f3c2528
feat(kakao): add BMS_FREE message type and BMS storage types
Palbahngmiyine 0ed40eb
feat(kakao): add BMS Free enum types (ChatBubbleType, ButtonType)
Palbahngmiyine 97c2c4a
feat(kakao): add BMS Free data classes (Button, Commerce, Coupon, Video)
Palbahngmiyine f5d2c73
feat(kakao): add BMS Free WideItem and Carousel models
Palbahngmiyine f733370
feat(kakao): expand KakaoBmsOption with all BMS Free fields
Palbahngmiyine f37b7a6
test(kakao): add BMS Free serialization tests
Palbahngmiyine 54cdec3
Update gitignore rule
Palbahngmiyine 7175035
test(kakao): add BMS Free E2E test suite
Palbahngmiyine df8e0e3
test(e2e): improve error output to display failedMessageList details
Palbahngmiyine 3ae6ba5
fix(e2e): correct BMS Free test field usage and StorageType mapping
Palbahngmiyine d7c974b
feat(dto): add LocalDateTime support for date fields
Palbahngmiyine e2889f6
build: add kotlinx.serialization to shadow JAR relocate list
Palbahngmiyine 7078b20
Remove NurigoApp.kt
Palbahngmiyine d74385d
refactor: extract BaseE2ETest and E2ETestUtils for E2E test infrastru…
Palbahngmiyine 756741a
test(e2e): add comprehensive E2E tests for solapi-kotlin SDK
Palbahngmiyine b20f05c
refactor: use stdlib toKotlinInstant extension for LocalDateTime conv…
Palbahngmiyine 510b0ad
refactor: extract uploadImage utility to BaseE2ETest
Palbahngmiyine 1c8f412
refactor: make BmsFreeE2ETest extend BaseE2ETest
Palbahngmiyine c170e1e
refactor: make ScheduledMessageE2ETest extend BaseE2ETest
Palbahngmiyine 6e1937a
refactor: add multi-module auto-discovery to settings.gradle.kts
Palbahngmiyine 31a8369
feat: add Java and Kotlin example modules
Palbahngmiyine 3b1ce5d
docs: rewrite README with comprehensive SDK documentation
Palbahngmiyine c21dbb8
refactor: remove FriendTalk (CTA/CTI) support
Palbahngmiyine f365063
test: restore FriendTalk E2E tests for backward compatibility
Palbahngmiyine 514528e
docs: prioritize Java examples in README
Palbahngmiyine b260cca
docs: add Java to README title
Palbahngmiyine 13cd593
docs: update environment variables table formatting and description
Palbahngmiyine 9cddef5
docs: add JDK 8 user guide and improve code examples
Palbahngmiyine 83d6ce8
docs: add AI quick start guide section to README
Palbahngmiyine ce638aa
Update README.md
Palbahngmiyine f263440
Update README.md
Palbahngmiyine c5aa9f8
docs: add LLM guide and version sync hook
Palbahngmiyine 82abfa1
docs: add JDK 8 compatible Java examples to LLM guide
Palbahngmiyine 1fcf092
refactor: remove explicit ATA type from Kakao Alimtalk examples
Palbahngmiyine 6614de8
docs: add Agent Workflow section and translate LLM guide to English
Palbahngmiyine ad1ace1
refactor: consolidate Kotlin compile tasks and update version require…
Palbahngmiyine e7d6838
build: auto-exclude example modules during publish tasks
Palbahngmiyine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| #!/bin/bash | ||
| # 버전 동기화 스크립트 | ||
| # build.gradle.kts의 버전이 변경되면 README.md와 LLM_GUIDE.md에 반영 | ||
|
|
||
| set -e | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" | ||
|
|
||
| GRADLE_FILE="$PROJECT_ROOT/build.gradle.kts" | ||
| README_FILE="$PROJECT_ROOT/README.md" | ||
| LLM_GUIDE_FILE="$PROJECT_ROOT/LLM_GUIDE.md" | ||
|
|
||
| # build.gradle.kts가 수정된 경우에만 실행 | ||
| if [[ -n "$CLAUDE_FILE_PATHS" ]]; then | ||
| if ! echo "$CLAUDE_FILE_PATHS" | grep -q "build.gradle.kts"; then | ||
| exit 0 | ||
| fi | ||
| fi | ||
|
|
||
| # build.gradle.kts에서 버전 추출 (예: version = "1.1.0" -> 1.1.0) | ||
| VERSION=$(grep -E '^version\s*=' "$GRADLE_FILE" | head -1 | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/') | ||
|
|
||
| if [[ -z "$VERSION" ]] || [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | ||
| echo "유효한 버전을 찾을 수 없습니다: $VERSION" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # README.md 버전 업데이트 | ||
| if [[ -f "$README_FILE" ]]; then | ||
| # com.solapi:sdk:X.X.X 패턴 업데이트 | ||
| sed -i '' -E "s/(com\.solapi:sdk:)[0-9]+\.[0-9]+\.[0-9]+/\1$VERSION/g" "$README_FILE" | ||
| # <version>X.X.X</version> 패턴 | ||
| sed -i '' -E "s|(<version>)[0-9]+\.[0-9]+\.[0-9]+(</version>)|\1$VERSION\2|g" "$README_FILE" | ||
| fi | ||
|
|
||
| # LLM_GUIDE.md 버전 업데이트 | ||
| if [[ -f "$LLM_GUIDE_FILE" ]]; then | ||
| sed -i '' -E "s/(com\.solapi:sdk:)[0-9]+\.[0-9]+\.[0-9]+/\1$VERSION/g" "$LLM_GUIDE_FILE" | ||
| sed -i '' -E "s|(<version>)[0-9]+\.[0-9]+\.[0-9]+(</version>)|\1$VERSION\2|g" "$LLM_GUIDE_FILE" | ||
| fi | ||
|
|
||
| echo "버전 $VERSION 동기화 완료" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # SOLAPI API Credentials | ||
| SOLAPI_API_KEY= | ||
| SOLAPI_API_SECRET= | ||
|
|
||
| # Kakao Business Channel | ||
| KAKAO_PF_ID= | ||
|
|
||
| # Phone Numbers (Optional) | ||
| SENDER_NUMBER= | ||
| TEST_PHONE_NUMBER= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| # SOLAPI Kotlin SDK - Knowledge Base | ||
|
|
||
| **Generated:** 2026-01-27 | **Commit:** 618f129 | **Branch:** main | ||
|
|
||
| ## CRITICAL: Development Principles | ||
|
|
||
| **MUST follow `CLAUDE.md` development principles:** | ||
|
|
||
| | Principle | Rule | | ||
| |-----------|------| | ||
| | **Tidy First** | NEVER mix structural and behavioral changes in a single commit | | ||
| | **Commit Separation** | `refactor:` (structural) vs `feat:`/`fix:` (behavioral) in separate commits | | ||
| | **TDD** | Write tests first (Red → Green → Refactor) | | ||
| | **Single Responsibility** | Classes/methods have single responsibility only | | ||
| | **Tidy Code First** | Clean up target area code before adding features | | ||
|
|
||
| ```bash | ||
| # Correct commit order | ||
| git commit -m "refactor: extract validation logic to separate method" | ||
| git commit -m "feat: add phone number format validation" | ||
|
|
||
| # Forbidden (mixed commit) | ||
| git commit -m "feat: add validation and refactor code" # ❌ FORBIDDEN | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## OVERVIEW | ||
|
|
||
| Kotlin/Java SDK for SOLAPI messaging platform. Supports SMS, LMS, MMS, Kakao Alimtalk/Brand Message, Naver Smart Notification, RCS, Fax, and Voice messaging. | ||
|
|
||
| ## STRUCTURE | ||
|
|
||
| ``` | ||
| src/main/java/com/solapi/sdk/ | ||
| ├── SolapiClient.kt # Entry point (use this) | ||
| ├── NurigoApp.kt # DEPRECATED - do not use | ||
| └── message/ | ||
| ├── service/ # API operations (send, query, templates) | ||
| ├── model/ # Domain models (Message, options) | ||
| │ └── kakao/ # 19 files - Kakao templates, buttons, options | ||
| ├── dto/ # Request/Response DTOs | ||
| ├── exception/ # Exception hierarchy (8 types) | ||
| └── lib/ # Internal utilities (auth, helpers) | ||
| ``` | ||
|
|
||
| ## WHERE TO LOOK | ||
|
|
||
| | Task | Location | Notes | | ||
| |------|----------|-------| | ||
| | **Initialize SDK** | `SolapiClient.kt` | `createInstance(apiKey, secretKey)` | | ||
| | **Send messages** | `service/DefaultMessageService.kt` | `send(message)` or `send(messages)` | | ||
| | **Query messages** | `service/DefaultMessageService.kt` | `getMessageList(params)` | | ||
| | **Upload files** | `service/DefaultMessageService.kt` | `uploadFile(file, type)` for MMS/Fax | | ||
| | **Kakao Alimtalk** | `service/DefaultMessageService.kt` | 11 template methods | | ||
| | **Create Message** | `model/Message.kt` | Data class with all message options | | ||
| | **Kakao options** | `model/kakao/KakaoOption.kt` | Alimtalk/FriendTalk config | | ||
| | **Handle errors** | `exception/` | Catch specific `Solapi*Exception` types | | ||
| | **HTTP layer** | `service/MessageHttpService.kt` | Retrofit interface (internal) | | ||
| | **Auth** | `lib/Authenticator.kt` | HMAC-SHA256 (internal, auto-injected) | | ||
|
|
||
| ## CODE PATTERNS | ||
|
|
||
| ### Serialization | ||
| ```kotlin | ||
| @Serializable | ||
| data class Message( | ||
| var to: String? = null, | ||
| var from: String? = null, | ||
| // All fields nullable with defaults for flexibility | ||
| ) | ||
| ``` | ||
| - **ALWAYS** use `@Serializable` annotation | ||
| - **ALWAYS** use `kotlinx.serialization` (not Jackson/Gson) | ||
| - **ALWAYS** provide nullable fields with defaults | ||
|
|
||
| ### Service Methods | ||
| ```kotlin | ||
| @JvmOverloads // Java interop | ||
| @Throws(SolapiMessageNotReceivedException::class, ...) | ||
| fun send(messages: List<Message>, config: SendRequestConfig? = null): MultipleDetailMessageSentResponse | ||
| ``` | ||
| - **ALWAYS** annotate with `@JvmOverloads` for optional params | ||
| - **ALWAYS** declare `@Throws` for checked exceptions | ||
|
|
||
| ### Exception Handling | ||
| ```kotlin | ||
| // Internal: Map error codes to exceptions | ||
| when (errorResponse.errorCode) { | ||
| "ValidationError" -> throw SolapiBadRequestException(msg) | ||
| "InvalidApiKey" -> throw SolapiInvalidApiKeyException(msg) | ||
| else -> throw SolapiUnknownException(msg) | ||
| } | ||
| ``` | ||
| - Exceptions are `sealed interface` based (SolapiException) | ||
| - 8 specific exception types | ||
|
|
||
| ### Phone Number Normalization | ||
| ```kotlin | ||
| init { | ||
| from = from?.replace("-", "") | ||
| to = to?.replace("-", "") | ||
| } | ||
| ``` | ||
| - Dashes auto-stripped from phone numbers in `Message.init` | ||
|
|
||
| ### Test Conventions | ||
| ```kotlin | ||
| import kotlin.test.Test | ||
| import kotlin.test.assertEquals | ||
| import kotlin.test.assertTrue | ||
| import kotlin.test.assertFailsWith | ||
|
|
||
| class AuthenticatorTest { | ||
| @Test | ||
| fun `generateAuthInfo returns HMAC-SHA256 format`() { | ||
| // Given | ||
| val authenticator = Authenticator("api-key", "secret") | ||
| // When | ||
| val result = authenticator.generateAuthInfo() | ||
| // Then | ||
| assertTrue(result.startsWith("HMAC-SHA256 ")) | ||
| } | ||
| } | ||
| ``` | ||
| - **ALWAYS** use `kotlin.test` (NOT JUnit directly) | ||
| - **ALWAYS** use Given-When-Then comment structure | ||
| - **ALWAYS** use backtick method names for readability | ||
|
|
||
| ## ANTI-PATTERNS | ||
|
|
||
| | Forbidden | Required | | ||
| |-----------|----------| | ||
| | `NurigoApp.initialize()` | `SolapiClient.createInstance()` | | ||
| | Direct `DefaultMessageService()` | Use factory via `SolapiClient` | | ||
| | Catch generic `Exception` | Catch specific `Solapi*Exception` | | ||
| | `net.nurigo.sdk` imports | `com.solapi.sdk` package only | | ||
| | Jackson/Gson serialization | `kotlinx.serialization` only | | ||
| | Mixed structural+behavioral commits | Separate commits per Tidy First | | ||
|
|
||
| ## INTERNAL CLASSES (Do Not Use Directly) | ||
|
|
||
| - `Authenticator` - HMAC auth (auto-injected via interceptor) | ||
| - `ErrorResponse` - Internal error DTO | ||
| - `MessageHttpService` - Retrofit interface | ||
| - `JsonSupport` - Serialization config | ||
| - `MapHelper`, `Criterion` - Internal utilities | ||
|
|
||
| ## EXCEPTION TYPES | ||
|
|
||
| | Exception | When Thrown | | ||
| |-----------|-------------| | ||
| | `SolapiApiKeyException` | Empty/missing API key | | ||
| | `SolapiInvalidApiKeyException` | Invalid credentials | | ||
| | `SolapiBadRequestException` | Validation error, bad input | | ||
| | `SolapiEmptyResponseException` | Server returned empty body | | ||
| | `SolapiFileUploadException` | File upload failed | | ||
| | `SolapiMessageNotReceivedException` | All messages failed (has `failedMessageList`) | | ||
| | `SolapiUnknownException` | Unclassified server error | | ||
|
|
||
| ## KAKAO INTEGRATION | ||
|
|
||
| 19 model files in `model/kakao/`: | ||
| - `KakaoOption` - Main config (pfId, templateId, variables) | ||
| - `KakaoAlimtalkTemplate*` - Template CRUD models | ||
| - `KakaoBrandMessageTemplate` - Brand message with carousels | ||
| - `KakaoButton`, `KakaoButtonType` - Button configurations | ||
|
|
||
| Template workflow: `getKakaoAlimtalkTemplateCategories()` → `createKakaoAlimtalkTemplate()` → `requestKakaoAlimtalkTemplateInspection()` | ||
|
|
||
| ## BUILD & TEST | ||
|
|
||
| ```bash | ||
| ./gradlew clean build test # Full build | ||
| ./gradlew test # Tests only | ||
| ./gradlew shadowJar # Fat JAR with relocated deps | ||
| ``` | ||
|
|
||
| **Shadow JAR**: Dependencies relocated to `com.solapi.shadow.*` to prevent conflicts. | ||
|
|
||
| ## NOTES | ||
|
|
||
| - **Java 8 target**: Code must work on JVM 1.8 | ||
| - **Source location**: Kotlin in `src/main/java/` (unconventional but intentional) | ||
| - **Tests**: `src/test/kotlin/`, `kotlin.test` (Kotlin native), Given-When-Then style | ||
| - **Version**: Auto-generated at `build/generated/source/kotlin/com/solapi/sdk/Version.kt` | ||
| - **Docs**: Dokka output to `./docs/`, run `./gradlew dokkaGeneratePublicationHtml` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.