feat: Add carrier-connector handler package with carrier info, connectivity check, and shared types for GSMA ES2+ profile management

- Add handlers package with GetCarrierInfoHandler returning static carrier name/mcc/mnc/smdpUrl/supported/lastChecked from environment config
- Add CheckConnectivityHandler with ES2+ GetProfileStatus probe using synthetic EID/ICCID and smdpConnected/checkedAt/error response
- Add ProfileOrder struct with EID/ICCID/IMSI/K/OPc/MCC/MNC/ProfileType/ConfirmationCode fields
This commit is contained in:
nutcas3
2026-04-24 08:50:22 +03:00
parent 5f6d58a0b7
commit d27709222e
2 changed files with 111 additions and 0 deletions
@@ -0,0 +1,72 @@
// Package handlers hosts HTTP handlers for the carrier-connector service.
//
// Profile handlers (order, get, list, delete) live in profile_handlers.go and
// take a repository.ProfileRepository so profile state can be persisted and
// queried. Carrier-level handlers (info, connectivity) that only touch the
// ES2+ client live here.
package handlers
import (
"context"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/es2"
)
// GetCarrierInfoHandler returns static carrier information from environment config.
func GetCarrierInfoHandler(_ *es2.ES2Client) gin.HandlerFunc {
return func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"success": true,
"carrier": gin.H{
"name": GetEnv("CARRIER_NAME", "Example Carrier"),
"mcc": GetEnv("CARRIER_MCC", "208"),
"mnc": GetEnv("CARRIER_MNC", "93"),
"smdpUrl": GetEnv("SMDP_URL", "https://smdp.example.com"),
"supported": true,
"lastChecked": time.Now().UTC(),
},
})
}
}
// CheckConnectivityHandler probes the SM-DP+ via an ES2+ GetProfileStatus call
// with synthetic identifiers. A non-error response indicates the endpoint is
// reachable and accepting authenticated requests.
func CheckConnectivityHandler(client *es2.ES2Client) gin.HandlerFunc {
return func(c *gin.Context) {
req := &es2.GetProfileStatusRequest{EID: "connectivity-probe", ICCID: "connectivity-probe"}
_, err := client.GetProfileStatus(context.Background(), req)
connected := err == nil
var errMsg interface{}
if err != nil {
errMsg = err.Error()
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"connectivity": gin.H{
"smdpConnected": connected,
"checkedAt": time.Now().UTC(),
"error": errMsg,
},
})
}
}
// parsePositiveInt parses a positive integer from a string, returning fallback on failure.
func parsePositiveInt(s string, fallback int) int {
if s == "" {
return fallback
}
n, err := strconv.Atoi(s)
if err != nil || n <= 0 {
return fallback
}
return n
}
@@ -0,0 +1,39 @@
package handlers
import (
"os"
"github.com/rs/zerolog"
)
// Logger is the package-level logger, initialized by main.
var Logger zerolog.Logger
// ProfileOrder is the GSMA ES2+ profile order request accepted by the API.
type ProfileOrder struct {
EID string `json:"eid"`
ICCID string `json:"iccid"`
IMSI string `json:"imsi"`
K string `json:"k"`
OPc string `json:"opc"`
MCC string `json:"mcc"`
MNC string `json:"mnc"`
ProfileType string `json:"profileType"`
ConfirmationCode string `json:"confirmationCode,omitempty"`
}
// ProfileResponse is the GSMA ES2+ profile order response returned by the API.
type ProfileResponse struct {
ExecutionStatus string `json:"executionStatus"`
StatusMessage string `json:"statusMessage"`
ProfileID string `json:"profileId"`
ActivationCode string `json:"activationCode,omitempty"`
}
// GetEnv returns the value of an environment variable or fallback if unset.
func GetEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}