tzot tzot - 1 month ago 12
reST (reStructuredText) Question

Azure Job submission fails with 400 complaining about Etag

Prologue



I'm trying to create a “ScheduleUpdateTwin” job to Azure IoT hub using the Azure REST API, using Python 2 and the
requests
package.

Analysis



response to the request



I'll start with the
requests
log line (hiding the hubname):

DEBUG:urllib3.connectionpool:https://<myhubname>.azure-devices.net:443 "PUT /jobs/v2/j1505904104?api-version=2017-06-30 HTTP/1.1" 400 441


Indeed, the
req.status_code
is
400
.

The
req.content
is (mangling the tracking IDs a little):


{"Message":"ErrorCode:ArgumentInvalid;Error: BadRequest {\"Message\":\"ErrorCode:ArgumentInvalid;Missing or invalid etag for job type ScheduleUpdateTwin. ScheduleUpdateTwin job type is a force update, which only accepts \'*\' as the Etag.\",\"ExceptionMessage\":\"Tracking ID:xxxxxxxxxx8844a48f81e29359ba0279-TimeStamp:09/20/2017 10:41:45\"}","ExceptionMessage":"Tracking ID:xxxxxxxxxxe14888a51434047aef23d0-G:5-TimeStamp:09/20/2017 10:41:46"}


response headers



{'Content-Length': '441',
'Content-Type': 'application/json; charset=utf-8',
'Date': 'Wed, 20 Sep 2017 11:10:12 GMT',
'Server': 'Microsoft-HTTPAPI/2.0',
'iothub-errorcode': 'ArgumentInvalid'}


request headers



The headers of my request (as sent to the Azure API endpoint) are (again, mangling private data):

{'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Authorization': 'SharedAccessSignature '
'sr=<myhubname>.azure-devices.net%2Fjobs&skn=<skn>&sig=<sig>&se=<se>',
'Connection': 'keep-alive',
'Content-Length': '227',
'Content-Type': 'application/json',
'If-Match': '*',
'User-Agent': 'python-requests/2.18.1'}


request body



Here's the (pretty-printed for posting here, so the Content-Length would be different, obviously) request body:

{
"maxExecutionTimeInSeconds": 3600,
"updateTwin": {
"properties": {
"desired": {
"version": "5.0.0"
}
},
"tags": {
"owner": "tzot"
}
},
"jobId": "j1505904104",
"type": "scheduleUpdateTwin",
"queryCondition": "deviceId = 'test_rpi_01'"
}


If-None-Match instead of If-Match



Of course, the documentation hints that
If-None-Match
should be provided, since this is a
PUT
. So I alter my code and send an
If-None-Match: *
instead; the response is a
400
again.

Other REST API calls work fine



Please note that other REST API calls, like
devices/{device_id}
work fine for
GET
,
PUT
and
DELETE
operations without an Etag set; same goes for
twins/{device_id}
(
GET
,
PATCH
) and
twins/{device_id}/methods
(
POST
).

API Version



The
API
version I use is:
api-version=2017-06-30


Epilogue



How do I submit a job to Azure IoT through a REST API call?

Updates



add "etag": "*" in the request JSON



After a suggestion by Rita Han, I added the
"etag": "*"
key to the request body, which became (note: new jobId):

{
"jobId": "j1505986505",
"maxExecutionTimeInSeconds": 3600,
"etag": "*",
"updateTwin": {
"properties": {
"desired": {
"version": "5.0.0"
}
},
"tags": {
"owner": "tzot"
}
},
"type": "scheduleUpdateTwin",
"queryCondition": "deviceId = 'test_rpi_01'"
}


The result is the same: HTTP 400 status code, with the same content in the response. Tried it with
If-Match: *
in the HTTP request headers, then with
If-None-Match: *
, then with
If-None-Match: blabla
, hoping that no existing ETag would match with “blabla”. All to no avail. The response is the same.

Solution



The content should include an
etag
property right inside the
updateTwin
value.

Answer Source

As the error information points out:

Missing or invalid etag for job type ScheduleUpdateTwin. ScheduleUpdateTwin job type is a force update, which only accepts \'*\' as the Etag.

You can modify your request body like this:

{
 "maxExecutionTimeInSeconds": 3600,
 "updateTwin": {
  "properties": {
   "desired": {
    "version": "5.0.0"
   }
  },
 "etag":"*",
 },
 "jobId": "j1505904104",
 "type": "scheduleUpdateTwin",
 "queryCondition": "deviceId = 'test_rpi_01'"
}

Update:

Add python test result:

enter image description here