Hi all,
I have been working on a library, a Go port on a part of FreePBX on and off since last year, specifically on the modules by taking configuration input and turn them into an Asterisk text output.
Currently it can do the following
- dialplan
- announcement
- callrecording
- daynight
- ivr
- parking
- ringgroup
- systemrecording
The plan is to port the following modules and write more unit tests to cover more input combinations.
- queue
- extensions
- feature codes
- time conditions
- time groups
- misc destinations
- trunks
It is 100% in Go language. I have 129 unit tests so far, covering all files at 89% coverage. Some snippets:
(repos not pushed yet)
import (
"fmt"
arw "github.com/gmhafiz/asterisk_rw"
"github.com/gmhafiz/asterisk_rw/module"
)
func main() {
// create a new announcement input request.
a := &module.Announcement{
ID: 1,
Description: "test description",
CallRecordingFileName: "test.WAV",
AllowSkip: false,
PostDestination: "app-blackhole,hangup,1,",
ReturnIvr: false,
NoAnswer: false,
RepeatMessage: "",
}
// create an asterisk configuration setting
created, _ := a.Create()
fmt.Println(created)
}
output
[app-Announcement-1]
exten => s,1,GotoIf($["${CHANNEL(state)}" = "Up"]?begin)
exten => s,n,Answer
exten => s,n,Wait(1)
exten => s,n(begin),Noop(test description)
exten => s,n,Playback(custom/test.WAV,noanswer)
exten => s,n,Goto(app-blackhole,hangup,1,)
For unit tests, the test cases are designed around table-driven unit tests. Once the cases are added to the table, we loop through and then compare the expected strings against the output.
type test struct {
name string
input Announcement
want string
}
tableTests := []test{
{
"no answer",
Announcement{
ID: 1,
Description: "a1",
CallRecordingID: 1,
CallRecordingFileName: "file_example_WAV_1MG",
AllowSkip: false,
PostDestination: "app-blackhole,hangup,1",
ReturnIvr: false,
NoAnswer: false,
RepeatMessage: "", // disable
},
},
}
for _, test := range tableTests {
t.Run(test.name, func(t *testing.T) {
got, err := test.input.Create()
if err != nil {
t.Error(err)
}
if got != test.want {
fmt.Println("got:")
fmt.Println(got)
fmt.Println("want:")
fmt.Println(test.want)
t.Error()
}
})
}
The Create()
method is done by translating from the freepbx modules:
const (
extensionS = "s"
)
// Create first creates an in-memory model representation of the data before
// returning (string, error)
func (a *Announcement) Create() (string, error) {
var ext extension.Extension
section := a.CreateSection()
ext.AddSection(section)
if !a.NoAnswer {
ext.Add(conf.New(section, extensionS, "", dialplan.GotoIf(`$["${CHANNEL(state)}" = "Up"]`, "begin", "")))
ext.Add(conf.New(section, extensionS, "", dialplan.Answer()))
ext.Add(conf.New(section, extensionS, "", dialplan.Wait(1)))
} else {
ext.Add(conf.New(section, extensionS, "", dialplan.Progress()))
}
}
etc...
Thanks to the statically typed language, we can make sure each parameter of the methods are of correct types.
IDE on the other hand, helps with empty strings
The reason why we are porting to Go is that our stack is primarily in that language. Porting this, removes the dependency of a whole freepbx project as I primarily only need the modules functionalities.
I am going to publish this library. My question is that some modules are GPL while others are AGPL like callrecording module. I have all the modules in one folder and I included the GPL LICENSE
file. I am not sure if I have to rename the AGPL LICENSE
file when including it.
module
|
-- extension
|
-- announcement.go
-- announcement_test.go
-- callrecording.go
-- callrecording_test.go
-- dayNight.go
-- dayNight_test.go
-- LICENSE
-- etc...
An alternative is to make a directory for each module so that I can put the correct LICENSE
file in each folder.
On top of this library, I also have a gRPC(and HTTP) server project that includes this library. The purpose of this one is to act as a microservice, and it interacts with database. So that will be open source too. I am not sure on where to place these LICENSE
files.
One other thing is the name of this library, asterisk_rw
which people could confuse that it is an official product from Sangoma. I believe it could violate Sangoma’s copyright. As I haven’t pushed this repository yet, I can still change the name of this library.