This document expands on some of the technical details of SMSSync Issue 11 and proposes a solution for communicating results of outgoing SMS messages processsed by SMSSync.
SMSSync has a basic HTTP client in MainHttpClient.java it should suffice for the purposes of this feature.
Issue 120 should be completed first, see https://gist.github.com/mandric/11287748.
A new setting should be added, a boolean for now like "Enable Message Results API" unless someone can think of better name. This allows support for the HTTP API to be optional, otherwise the sms send logic and server requirments remain backwards compatible.
To get a list of messages to send SMSSync will make a request using GET and
passing the parameter task=send
to the Sync URL. The server should respond
with the following changes to the current payload JSON structure:
message_uuid
a UUID generated by the server for outgoing messages, additional property in the messages objects.
If this property is not present in a message then we can default to current behavior; results are not communicated to the server. A UUID is required for us to sync message result data.
When boolean setting is true, parse response and loop through the list of
messages making a POST queued_messages
request and then saving and sending
the message once the server acknwledges it.
SMSSync would ignore the response and let the task checking continue on next interval.
GET /api/smssync?task=send HTTP/1.1
Host: testserver.local
HTTP/1.1 200 OK
Server: nginx/1.5.2
Content-Type: application/json; charset=utf-8
{
"payload": {
"task": "send",
"secret": "",
"messages": [
{
"to": "+254700709142",
"message": "first message goes here",
"message_uuid": "aada21b0-0615-4957-bcb3-71fb766aaa9a"
},
{
"to": "+254700709142",
"message": "second message goes here",
"message_uuid": "1ba368bd-c467-4374-bf28-2bcf648cfa6d"
},
{
"to": "+254700709142",
"message": "third message goes here",
"message_uuid": "95df126b-ee80-4175-a6fb-3d481ef3ef49"
}
]
}
}
For each message or group of messages in the outgoing task list, SMSSync will send an HTTP request to acknowledge messages are being processed, by using the following JSON structure:
queued_messages
array of UUIDs of the messages being sent.
Note: In practice only one item will be in the list becasue we will be
calling this once per message while looping over the GET ?task=send
response
body.
When the the server receives the list of message IDs it would modify some state information in the database so the messages that are being processed by SMSSync do not appear in the outgoing task list anymore. The server might have a process that checks periodically to reprocess messages that have gone stale and never received an update from SMSSync.
This helps to avoid a race condition when two or more SMSSyncs are processing messages on the same server. If another SMSSync has already started processing a specfic ID then it would not be included in the response body.
The server response should be 200 on success and include the list of message IDs in the response as a confirmation those messages should be processed.
message_uuids
- array of message UUIDs that are ready to be sent.
The server has acknowledges so now we can call ProcessSms.sendSMS
for each
message in the list, and save message data including result properites and
broadcasting the sent and deliver intents.
If 200 status code is not received then SMSSync should stop processing the list of messages and let the task polling continue on the next interval.
POST /api/smssync?task=sent HTTP/1.1
Host: testserver.local
Content-Type: application/json; charset=utf-8
{
"queued_messages": [
"aada21b0-0615-4957-bcb3-71fb766aaa9a",
"1ba368bd-c467-4374-bf28-2bcf648cfa6d",
"95df126b-ee80-4175-a6fb-3d481ef3ef49"
]
}
HTTP/1.1 200 OK
Server: nginx/1.5.2
Content-Type: application/json; charset=utf-8
{
"message_uuids": [
"aada21b0-0615-4957-bcb3-71fb766aaa9a",
"1ba368bd-c467-4374-bf28-2bcf648cfa6d",
"95df126b-ee80-4175-a6fb-3d481ef3ef49"
]
}
POST /api/smssync?task=sent HTTP/1.1
Host: testserver.local
Content-Type: application/json; charset=utf-8
{
"queued_messages": [
"aada21b0-0615-4957-bcb3-71fb766aaa9a",
"1ba368bd-c467-4374-bf28-2bcf648cfa6d",
"95df126b-ee80-4175-a6fb-3d481ef3ef49"
]
}
HTTP/1.1 200 OK
Server: nginx/1.5.2
Content-Type: application/json; charset=utf-8
{
"message_uuids": []
}
When the boolean setting is true, SMSSync should use the task checking frequency to
poll this URL, the same way as ?task=send
, but with ?task=results
parameters
the server should return a list of message UUIDs that are waiting to receive
result data. Similarly as ?task=send
the server will manage the load it
passes to SMSSync.
message_uuids
- array of message UUIDs that are ready to be sent.
SMSSync should parse response and loop through the list of messages ids and
collect result data for all the messages that has been saved locally and
construct a request for POST ?task=results message_results
API call.
SMSSync would ignore the response and let the task checking continue on next interval.
GET /api/smssync?task=results HTTP/1.1
Host: testserver.local
HTTP/1.1 200 OK
Server: nginx/1.5.2
Content-Type: application/json; charset=utf-8
{
"message_uuids": [
"aada21b0-0615-4957-bcb3-71fb766aaa9a",
"1ba368bd-c467-4374-bf28-2bcf648cfa6d",
"95df126b-ee80-4175-a6fb-3d481ef3ef49"
]
}
To send result data to the server, create objects out of the message property names, and append them to an array. For example:
message_results
- the name of the array contains the result objects.uuid
- the UUID of the message the results correspond to.sent_result_code
- property value on the message.sent_result_message
- property value on the message.delivery_result_code
- property value on the message.delivery_result_message
- property value on the message.
SMSSync is done communicating result data back to server.
Then skip and let polling continue.
POST /api/smssync?task=results HTTP/1.1
Host: testserver.local
Content-Type: application/json; charset=utf-8
{
"message_results": [
{
"uuid": "052bf515-ef6b-f424-c4ee-da7ee61ce81a",
"sent_result_code": 0,
"sent_result_message": "SMSSync Message Sent"
"delivered_result_code": -1,
"delivered_result_message": ""
},
{
"uuid": "aada21b0-0615-4957-bcb3-71fb766aaa9a",
"sent_result_code": 0,
"sent_result_message": "SMSSync Message Sent",
"delivered_result_code": 0,
"delivered_result_message": "SMS Delivered"
},
{
"uuid": "1ba368bd-c467-4374-bf28-2bcf648cfa6d",
"sent_result_code": 1,
"sent_result_message": "Failed to send SMS - Maybe insufficient air time on the phone.",
"delivered_result_code": -1,
"delivered_result_message": ""
},
{
"uuid": "95df126b-ee80-4175-a6fb-3d481ef3ef49",
"sent_result_code": 4,
"sent_result_message": "No service",
"delivered_result_code": -1,
"delivered_result_message": ""
},
...
]
}
I wonder what considerations we should make for Android 4.4 (API level 19) which has a more sophisticated Telephony API.
To avoid the race condition when two SMSSyncs are connected, we could return an error code on the second POST which would dequeue the message on the SMSSync side. If you did this you would need to fire one POST per message, rather than send a list, to make it clear which one failed.