simplify SaaS SIP configuration response
This commit is contained in:
@@ -38,10 +38,6 @@ type Snapshot struct {
|
||||
TenantID string
|
||||
TenantKey string
|
||||
SIPRevision int64
|
||||
SIPSnapshotSHA256 string
|
||||
SIPCellID string
|
||||
SIPArtifactRevision int64
|
||||
SIPArtifactConfigSHA256 string
|
||||
TaskRevision int64
|
||||
TaskStatus string
|
||||
TaskMaxConcurrentCalls int64
|
||||
@@ -122,10 +118,23 @@ func (c *Client) ReadTask(ctx context.Context, taskID, tenantID string) (Snapsho
|
||||
if err := json.Unmarshal(sipBody, &sip); err != nil {
|
||||
return Snapshot{}, fmt.Errorf("decode SIP configuration identity: %w", err)
|
||||
}
|
||||
if sip.Resource != resourceSIPConfig || sip.DispatcherID != c.dispatcherID || sip.Revision <= 0 || sip.SnapshotSHA256 == "" ||
|
||||
sip.Artifact.CellID == "" || sip.Artifact.Revision <= 0 || sip.Artifact.ConfigSHA256 == "" {
|
||||
if sip.Resource != resourceSIPConfig || sip.DispatcherID != c.dispatcherID || sip.Revision <= 0 {
|
||||
return Snapshot{}, errors.New("SIP configuration identity or revision does not match the request")
|
||||
}
|
||||
seenTrunks := make(map[string]bool, len(sip.Trunks))
|
||||
for _, trunk := range sip.Trunks {
|
||||
if seenTrunks[trunk.TrunkID] {
|
||||
return Snapshot{}, fmt.Errorf("SIP configuration repeats trunk %q", trunk.TrunkID)
|
||||
}
|
||||
seenTrunks[trunk.TrunkID] = true
|
||||
seenCallers := make(map[string]bool, len(trunk.CallerProfiles))
|
||||
for _, caller := range trunk.CallerProfiles {
|
||||
if seenCallers[caller.CallerProfileID] {
|
||||
return Snapshot{}, fmt.Errorf("SIP trunk %q repeats caller profile %q", trunk.TrunkID, caller.CallerProfileID)
|
||||
}
|
||||
seenCallers[caller.CallerProfileID] = true
|
||||
}
|
||||
}
|
||||
|
||||
taskPath := configReadPath + "/task/" + url.PathEscape(taskID)
|
||||
taskBody, err := c.getConfig(ctx, taskPath)
|
||||
@@ -165,10 +174,8 @@ func (c *Client) ReadTask(ctx context.Context, taskID, tenantID string) (Snapsho
|
||||
|
||||
return Snapshot{
|
||||
TaskID: taskID, TenantID: tenantID, TenantKey: task.TenantKey,
|
||||
SIPRevision: sip.Revision, SIPSnapshotSHA256: sip.SnapshotSHA256,
|
||||
SIPCellID: sip.Artifact.CellID, SIPArtifactRevision: sip.Artifact.Revision,
|
||||
SIPArtifactConfigSHA256: sip.Artifact.ConfigSHA256,
|
||||
TaskRevision: task.TaskRevision, TaskStatus: task.Status,
|
||||
SIPRevision: sip.Revision,
|
||||
TaskRevision: task.TaskRevision, TaskStatus: task.Status,
|
||||
TaskMaxConcurrentCalls: int64(task.MaxConcurrentCalls), TaskRingTimeoutMS: int64(task.RingTimeoutMS),
|
||||
TaskMaxCallDurationMS: int64(task.MaxCallDurationMS), TaskRoutePolicyID: task.RoutePolicyID,
|
||||
TaskCallerProfileID: task.CallerProfileID, TaskAllowedTrunkIDs: append([]string(nil), task.AllowedTrunkIDs...),
|
||||
@@ -307,15 +314,15 @@ func responseErrorCode(body []byte) string {
|
||||
}
|
||||
|
||||
type sipConfigResponse struct {
|
||||
Resource string `json:"resource"`
|
||||
DispatcherID string `json:"dispatcher_id"`
|
||||
Revision int64 `json:"revision"`
|
||||
SnapshotSHA256 string `json:"snapshot_sha256"`
|
||||
Artifact struct {
|
||||
CellID string `json:"cell_id"`
|
||||
Revision int64 `json:"revision"`
|
||||
ConfigSHA256 string `json:"config_sha256"`
|
||||
} `json:"artifact"`
|
||||
Resource string `json:"resource"`
|
||||
DispatcherID string `json:"dispatcher_id"`
|
||||
Revision int64 `json:"revision"`
|
||||
Trunks []struct {
|
||||
TrunkID string `json:"trunk_id"`
|
||||
CallerProfiles []struct {
|
||||
CallerProfileID string `json:"caller_profile_id"`
|
||||
} `json:"caller_profiles"`
|
||||
} `json:"trunks"`
|
||||
}
|
||||
|
||||
type taskConfigResponse struct {
|
||||
|
||||
@@ -67,6 +67,38 @@ func TestClientReadTaskRequestsAndValidatesThreeConfigEndpoints(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientReadTaskRejectsDuplicateSIPTrunksAndCallers(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
mutate func(map[string]any)
|
||||
}{
|
||||
{"duplicate trunk", func(sip map[string]any) {
|
||||
trunks := sip["trunks"].([]any)
|
||||
sip["trunks"] = append(trunks, trunks[0])
|
||||
}},
|
||||
{"duplicate caller", func(sip map[string]any) {
|
||||
trunk := sip["trunks"].([]any)[0].(map[string]any)
|
||||
profiles := trunk["caller_profiles"].([]any)
|
||||
trunk["caller_profiles"] = append(profiles, profiles[0])
|
||||
}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fixtures := validConfigFixtures(t)
|
||||
var sip map[string]any
|
||||
if err := json.Unmarshal(fixtures[configReadPath+"/sip"], &sip); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tc.mutate(sip)
|
||||
fixtures[configReadPath+"/sip"] = marshalDiscoveryResponse(t, sip)
|
||||
server := configFixtureServer(t, fixtures)
|
||||
defer server.Close()
|
||||
if _, err := newMockClient(t, server).ReadTask(context.Background(), mockTaskID, mockTenantID); err == nil {
|
||||
t.Fatal("duplicate SIP configuration identity accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientReadTaskRejectsSchemaInvalidResponse(t *testing.T) {
|
||||
fixtures := validConfigFixtures(t)
|
||||
fixtures[configReadPath+"/sip"] = readConfigFixture(t, "config-read-invalid-extra-property-v0.1.json")
|
||||
@@ -139,7 +171,7 @@ func newMockClient(t *testing.T, server *httptest.Server) *Client {
|
||||
func validConfigFixtures(t *testing.T) map[string][]byte {
|
||||
t.Helper()
|
||||
return map[string][]byte{
|
||||
configReadPath + "/sip": readConfigFixture(t, "config-read-sip-v0.1.json"),
|
||||
configReadPath + "/sip": readConfigFixture(t, "config-read-sip-v0.2.json"),
|
||||
configReadPath + "/tasks": readConfigFixture(t, "task-discovery-snapshot-v0.2.json"),
|
||||
configReadPath + "/task/" + mockTaskID: readConfigFixture(t, "config-read-task-v0.1.json"),
|
||||
configReadPath + "/tenant/" + mockTenantID + "/quota": readConfigFixture(t, "config-read-tenant-quota-v0.1.json"),
|
||||
|
||||
Reference in New Issue
Block a user