Compare commits
11 Commits
send-node
...
sohail/doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
95f6a735a0
|
|||
|
0bb44fc80b
|
|||
|
|
109a2a2020 | ||
|
|
bc6d8098f3
|
||
|
|
01e75e9217
|
||
|
|
f2b17880ba
|
||
|
|
1d4f116079
|
||
|
|
44c52b6ed7
|
||
|
|
454f67b317
|
||
|
|
e1506a3dcf
|
||
|
|
b22a4adec1 |
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
!/cmd/africastalking
|
||||||
|
!/common
|
||||||
|
!/config
|
||||||
|
!/initializers
|
||||||
|
!/internal
|
||||||
|
!/models
|
||||||
|
!/remote
|
||||||
|
!/services
|
||||||
|
!/LICENSE
|
||||||
|
!/README.md
|
||||||
|
!/go.*
|
||||||
|
!/.env
|
||||||
58
.github/workflows/docker.yaml
vendored
Normal file
58
.github/workflows/docker.yaml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Check out repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Cache Docker layers
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
- name: Login to GHCR Docker registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set outputs
|
||||||
|
run: |
|
||||||
|
echo "RELEASE_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV \
|
||||||
|
&& echo "RELEASE_SHORT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: ./
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
BUILD=${{ env.RELEASE_SHORT_COMMIT }}
|
||||||
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
|
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||||
|
tags: |
|
||||||
|
ghcr.io/grassrootseconomics/urdt-ussd:latest
|
||||||
|
ghcr.io/grassrootseconomics/urdt-ussd:${{ env.RELEASE_TAG }}
|
||||||
40
Dockerfile
Normal file
40
Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
FROM golang:1.23.0-bookworm AS build
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
ARG BUILDPLATFORM
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG BUILD=dev
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
COPY . .
|
||||||
|
RUN apt update && apt install libgdbm-dev
|
||||||
|
RUN git clone https://git.defalsify.org/vise.git go-vise
|
||||||
|
|
||||||
|
WORKDIR /build/services/registration
|
||||||
|
RUN echo "Compiling go-vise files"
|
||||||
|
RUN make VISE_PATH=/build/go-vise -B
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM"
|
||||||
|
RUN go mod download
|
||||||
|
RUN go build -o ussd-africastalking -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt update && apt install libgdbm-dev ca-certificates -y
|
||||||
|
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /service
|
||||||
|
|
||||||
|
COPY --from=build /build/ussd-africastalking .
|
||||||
|
COPY --from=build /build/LICENSE .
|
||||||
|
COPY --from=build /build/README.md .
|
||||||
|
COPY --from=build /build/services ./services
|
||||||
|
COPY --from=build /build/.env .
|
||||||
|
|
||||||
|
EXPOSE 7123
|
||||||
|
|
||||||
|
CMD ["./ussd-africastalking"]
|
||||||
@@ -29,8 +29,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
|
|
||||||
|
build = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -115,7 +117,7 @@ func main() {
|
|||||||
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
logg.Infof("start command", "build", build, "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
@@ -157,6 +159,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
lhs.SetDataStore(&userdataStore)
|
lhs.SetDataStore(&userdataStore)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
3
dev/dialoguss/sample_user.yaml
Normal file
3
dev/dialoguss/sample_user.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
url: http://localhost:7123
|
||||||
|
dial: "*384*96#"
|
||||||
|
phoneNumber: +254722123456
|
||||||
21
dev/docker-compose.yaml
Normal file
21
dev/docker-compose.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
services:
|
||||||
|
ussd-pg-store:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
user: postgres
|
||||||
|
environment:
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
volumes:
|
||||||
|
- ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
|
||||||
|
- ussd-pg:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:5432:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
volumes:
|
||||||
|
ussd-pg:
|
||||||
|
driver: local
|
||||||
1
dev/init_db.sql
Normal file
1
dev/init_db.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CREATE DATABASE urdt_ussd;
|
||||||
@@ -103,7 +103,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn
|
|||||||
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
||||||
ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
||||||
ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
|
ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
|
||||||
ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances)
|
ls.DbRs.AddLocalFunc("fetch_community_balance", ussdHandlers.FetchCommunityBalance)
|
||||||
ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher)
|
ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher)
|
||||||
ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers)
|
ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers)
|
||||||
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
|
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
|
||||||
|
|||||||
@@ -822,42 +822,14 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) FetchCommunityBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
code := codeFromCtx(ctx)
|
||||||
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
l := gotext.NewLocale(translationDir, code)
|
||||||
|
l.AddDomain("default")
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
//TODO:
|
||||||
if !ok {
|
//Check if the address is a community account,if then,get the actual balance
|
||||||
return res, fmt.Errorf("missing session")
|
res.Content = l.Get("Community Balance: 0.00")
|
||||||
}
|
|
||||||
symbol, _ := h.st.Where()
|
|
||||||
balanceType := strings.Split(symbol, "_")[0]
|
|
||||||
|
|
||||||
store := h.userdataStore
|
|
||||||
publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_api_error)
|
|
||||||
|
|
||||||
balance := balanceResponse.Balance
|
|
||||||
|
|
||||||
switch balanceType {
|
|
||||||
case "my":
|
|
||||||
res.Content = fmt.Sprintf("Your balance is %s", balance)
|
|
||||||
case "community":
|
|
||||||
res.Content = fmt.Sprintf("Your community balance is %s", balance)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package ussd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path"
|
"path"
|
||||||
@@ -1773,58 +1772,43 @@ func TestConfirmPin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFetchCustodialBalances(t *testing.T) {
|
func TestFetchCommunityBalance(t *testing.T) {
|
||||||
fm, err := NewFlagManager(flagsPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf(err.Error())
|
|
||||||
}
|
|
||||||
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
|
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
publicKey := "0X13242618721"
|
|
||||||
|
|
||||||
ctx, store := InitializeTestStore(t)
|
ctx, store := InitializeTestStore(t)
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
|
||||||
|
|
||||||
err = store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
balanceResponse *models.BalanceResult
|
languageCode string
|
||||||
expectedResult resource.Result
|
expectedResult resource.Result
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Test when fetch custodial balances is not a success",
|
name: "Test community balance content when language is english",
|
||||||
balanceResponse: &models.BalanceResult{
|
|
||||||
Balance: "0.003 CELO",
|
|
||||||
Nonce: json.Number("0"),
|
|
||||||
},
|
|
||||||
expectedResult: resource.Result{
|
expectedResult: resource.Result{
|
||||||
FlagReset: []uint32{flag_api_error},
|
Content: "Community Balance: 0.00",
|
||||||
},
|
},
|
||||||
|
languageCode: "eng",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
||||||
mockAccountService := new(mocks.MockAccountService)
|
mockAccountService := new(mocks.MockAccountService)
|
||||||
mockState := state.NewState(16)
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: store,
|
userdataStore: store,
|
||||||
flagManager: fm.parser,
|
|
||||||
st: mockState,
|
st: mockState,
|
||||||
accountService: mockAccountService,
|
accountService: mockAccountService,
|
||||||
}
|
}
|
||||||
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
// Set up the expected behavior of the mock
|
ctx = context.WithValue(ctx, "Language", lang.Language{
|
||||||
mockAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResponse, nil)
|
Code: tt.languageCode,
|
||||||
|
})
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte(""))
|
res, _ := h.FetchCommunityBalance(ctx, "fetch_community_balance", []byte(""))
|
||||||
|
|
||||||
//Assert that the result set to content is what was expected
|
//Assert that the result set to content is what was expected
|
||||||
assert.Equal(t, res, tt.expectedResult, "Result should match expected result")
|
assert.Equal(t, res, tt.expectedResult, "Result should match expected result")
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "1234",
|
"input": "1234",
|
||||||
"expectedContent": "Your balance is 0.003 CELO\n0:Back\n9:Quit"
|
"expectedContent": "Balance: {balance}\n\n0:Back\n9:Quit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "0",
|
"input": "0",
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "1234",
|
"input": "1234",
|
||||||
"expectedContent": "Your community balance is 0.003 CELO\n0:Back\n9:Quit"
|
"expectedContent": "{balance}\n0:Back\n9:Quit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "0",
|
"input": "0",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Salio la kikundi
|
{{.fetch_community_balance}}
|
||||||
@@ -1 +1 @@
|
|||||||
{{.fetch_custodial_balances}}
|
{{.fetch_community_balance}}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
LOAD reset_incorrect 6
|
LOAD reset_incorrect 6
|
||||||
LOAD fetch_custodial_balances 0
|
LOAD fetch_community_balance 0
|
||||||
CATCH api_failure flag_api_call_error 1
|
CATCH api_failure flag_api_call_error 1
|
||||||
MAP fetch_custodial_balances
|
MAP fetch_community_balance
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH pin_entry flag_account_authorized 0
|
CATCH pin_entry flag_account_authorized 0
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
|
|||||||
@@ -21,3 +21,6 @@ msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu limetumwa."
|
|||||||
|
|
||||||
msgid "Your request failed. Please try again later."
|
msgid "Your request failed. Please try again later."
|
||||||
msgstr "Ombi lako halikufaulu. Tafadhali jaribu tena baadaye."
|
msgstr "Ombi lako halikufaulu. Tafadhali jaribu tena baadaye."
|
||||||
|
|
||||||
|
msgid "Community Balance: 0.00"
|
||||||
|
msgid "Salio la Kikundi: 0.00"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{{.fetch_custodial_balances}}
|
{{.check_balance}}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
LOAD reset_incorrect 6
|
LOAD reset_incorrect 6
|
||||||
LOAD fetch_custodial_balances 0
|
LOAD check_balance 0
|
||||||
CATCH api_failure flag_api_call_error 1
|
CATCH api_failure flag_api_call_error 1
|
||||||
MAP fetch_custodial_balances
|
MAP check_balance
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH pin_entry flag_account_authorized 0
|
CATCH pin_entry flag_account_authorized 0
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
|
|||||||
Reference in New Issue
Block a user