Submitting a transaction via RESTful API (how to handle transactionid and timestamp) - hyperledger

Problem:
I am unable to POST a transaction via the RESTful API generated by the composer-rest-server. I am receiving statusCode 422; the transaction instance is not valid. However, the same example works in the Playground.
Scenario:
I've set up a transaction called Offer in my .cto file which posts an offer to buy a house:
// Offer - Specifies an offer that a bidder places on a house listing with an associated price
transaction Offer {
o Double bidPrice
--> HouseListing listing
--> Person bidder
}
The composer-rest-server has generated an API with the following JSON string to post a transaction of type Offer:
{
"$class": "org.acme.purchasing.Offer",
"bidPrice": 0,
"listing": "string",
"bidder": "string",
"transactionId": "string",
"timestamp": "2017-07-21T13:37:09.460Z"
}
I've since replaced this with a sample transaction using the following JSON code derived from the above example:
{
"$class": "org.acme.purchasing.Offer",
"bidPrice": 1000,
"listing": "001",
"bidder": "RJOHNSON",
"transactionId": "1b9aa63c-dfad-4aad-a610-dfc80f2796b2",
"timestamp": "2017-07-21T13:37:09.460Z"
}
The response returned is error code 422:
{
"error": {
"statusCode": 422,
"name": "ValidationError",
"message": "The `Offer` instance is not valid. Details: `transactionId` can't be set (value: \"1b9aa63c-dfad-4aad-a610-d...6b2\").",
"details": {
"context": "Offer",
"codes": {
"transactionId": [
"absence"
]
},
"messages": {
"transactionId": [
"can't be set"
]
}
},
"stack": "ValidationError: The `Offer` instance is not valid. Details: `transactionId` can't be set (value: \"1b9aa63c-dfad-4aad-a610-d...6b2\").\n at /usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/dao.js:355:12\n at ModelConstructor.<anonymous> (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/validations.js:566:11)\n at ModelConstructor.next (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/hooks.js:93:12)\n at ModelConstructor.<anonymous> (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/validations.js:563:23)\n at ModelConstructor.trigger (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/hooks.js:83:12)\n at ModelConstructor.Validatable.isValid (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/validations.js:529:8)\n at /usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/dao.js:351:9\n at doNotify (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:155:49)\n at doNotify (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:155:49)\n at doNotify (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:155:49)\n at doNotify (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:155:49)\n at Function.ObserverMixin._notifyBaseObservers (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:178:5)\n at Function.ObserverMixin.notifyObserversOf (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:153:8)\n at Function.ObserverMixin._notifyBaseObservers (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:176:15)\n at Function.ObserverMixin.notifyObserversOf (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:153:8)\n at Function.ObserverMixin._notifyBaseObservers (/usr/lib/node_modules/composer-rest-server/node_modules/loopback-datasource-juggler/lib/observer.js:176:15)"
}
}
Now the strange thing is that I've deployed the same BNA onto the Hyperledger Composer Playground and am able to execute transactions of type Offer successfully.
Note that in the Playground, "transactionId" and "timestamp" are not specified as the Playground appears to take care of these values. For example, this is what Playground proposes to me initially:
{
"$class": "org.acme.purchasing.Offer",
"bidPrice": 0,
"listing": "resource:org.acme.purchasing.HouseListing#id:7965",
"bidder": "resource:org.acme.purchasing.Person#id:4441"
}
Can anyone advise why it's saying the Offer instance is not valid? My first thought was that it's not liking the string I'm placing in "transactionId" but another Stack Overflow post points out that the transactionId is just an arbitrary UUIDv4 string which I've generated already.
Update #1: Failing even with default demo
In order to ensure by BNA is error-free, I've deployed the default carauction-demo (resembles my example closely) onto my local Hyperledger Fabric instance and deployed the composer-rest-server. I've also deployed the same BNA into the Playground. All assets and participants were created identical in both from the Explorer (local instance) and Playground. When it comes time to submit an Offer transaction:
{
"$class": "org.acme.vehicle.auction.Offer",
"bidPrice": 800,
"listing": "resource:org.acme.vehicle.auction.VehicleListing#L001",
"member": "resource:org.acme.vehicle.auction.Member#member3#acme.org"
}
This JSON was generated by the Playground and succeeds there. Copy/paste/executing into the Explorer yields a status 500 error.
{
"error": {
"statusCode": 500,
"name": "Error",
"message": "error trying invoke chaincode. Error: chaincode error (status: 500, message: Error: Object with ID 'string' in collection with ID 'Asset:org.acme.vehicle.auction.VehicleListing' does not exist)",
"stack": "Error: error trying invoke chaincode. Error: chaincode error (status: 500, message: Error: Object with ID 'string' in collection with ID 'Asset:org.acme.vehicle.auction.VehicleListing' does not exist)\n at _initializeChannel.then.then.then.then.catch (/usr/lib/node_modules/composer-rest-server/node_modules/composer-connector-hlfv1/lib/hlfconnection.js:806:34)"
}
}
I'm still at a lost as to what is wrong here.

After much experimentation and some searching, I've concluded that the problem was that NPM was installed using sudo (as root). I redid the installation as non-root and the problem has now been solved. Everything is working as expected.

Composer updates the transactionId itself as its generated, you cannot do this in your JSON and hence why you get the error. This is a Loopback -> Swagger conversion issue as it should not appear in a /POST REST operation - captured here https://github.com/hyperledger/composer/issues/663

So I have successfully executed Offer transactions (/POST). I think the '500' error is because there is a missing field ('string' type), or relationship in your Offer transaction via REST.
Using the example of the Car Auction network https://github.com/hyperledger/composer-sample-networks/blob/master/packages/carauction-network/models/auction.cto
Either of these Offer transactions using /POST were successful in Explorer:
{
"$class": "org.acme.vehicle.auction.Offer",
"bidPrice": 20,
"listing": "org.acme.vehicle.auction.VehicleListing#100",
"member": "org.acme.vehicle.auction.Member#a#b.com"
}
OR
{
"$class": "org.acme.vehicle.auction.Offer",
"bidPrice": 20,
"listing": "org.acme.vehicle.auction.VehicleListing#100",
"member": "org.acme.vehicle.auction.Member#a#b.com",
"timestamp": "2017-07-28T14:07:02.558Z"
}
responded with a 200 (SUCCESS) and a transactionId in Explorer:
{
"$class": "org.acme.vehicle.auction.Offer",
"bidPrice": 20,
"listing": "org.acme.vehicle.auction.VehicleListing#100",
"member": "org.acme.vehicle.auction.Member#a#b.com",
"transactionId": "e75b9934-1f08-4daf-90db-702bbe4b8fa1"
}
This is my VehicleListing asset JSON for #100 above
{
"$class": "org.acme.vehicle.auction.VehicleListing",
"listingId": "100",
"reservePrice": 50,
"description": "string",
"state": "FOR_SALE",
"offers": [
{
"$class": "org.acme.vehicle.auction.Offer",
"bidPrice": 50,
"listing": "100",
"member": "resource:org.acme.vehicle.auction.Member#a#b.com",
"transactionId": "string123",
"timestamp": "2017-07-28T14:07:02.825Z"
}
],
"vehicle": "resource:org.acme.vehicle.auction.Vehicle#123"
}
And I created asset for Vehicle 123 as well as Member a#b.com

Related

Google Slides API reports Invalid requests[0].updateTableCellProperties: Invalid field: table_cell_properties

Trying to troubleshoot an error message my app gets after sending a batchUpdate request to Google Slides API
Invalid requests[19].updateTableCellProperties: Invalid field: table_cell_properties
The 19th request in the batch is the only updateTableCellProperties request I have. If I removing the 19th request from the batch, everything works fine.
Other requests which I run in this batchUpdate with no issues are are insertTableRows, deleteTableRow, insertText, updateParagraphStyle, updateTextStyle, updateTableColumnProperties. They all work on the same table, so I use the same objectId, but depending on the request I have to specify it as tableObjectId instead of objectId.
Unsure if I am generating a wrong request for the only updateTableCellProperties request I have, or if there is a problem in the Google Slides ruby gem itself, I tried sending just this updateTableCellProperties request from the Google Slides API explorer which has some validation on the request structure. So I sent this updateTableCellProperties batchUpdate request
{
"requests": [
{
"updateTableCellProperties": {
"objectId": "gf9d8fea71f_22_1",
"tableRange": {
"location": {
"columnIndex": 0,
"rowIndex": 1
}
},
"fields": "tableCellProperties",
"tableCellProperties": {
"tableCellBackgroundFill": {
"solidFill": {
"color": {
"themeColor": "LIGHT1"
}
}
}
}
}
}
]
}
And I got this error:
{
"error": {
"code": 400,
"message": "Invalid requests[0].updateTableCellProperties: Invalid field: table_cell_properties",
"status": "INVALID_ARGUMENT"
}
}
Why is this updateTableCellProperties request reported as invalid? I am also confused by the output of the error message as it mentions table_cell_properties in snake case, while the documentation only mentions tableCellProperties in camel case, and my request also only mentions tableCellProperties in camel case. I am only aware of the ruby gems translating between snake case and camel case, but this is not relevant to the API Explorer.
The error Invalid field: table_cell_properties originates from the erroneously specified fields property
See documentation:
fields
At least one field must be specified. The root tableCellProperties is implied and should not be specified. A single "*" can be used as short-hand for listing every field.
So you need to modify fields
from
"fields": "tableCellProperties"
to
"fields": "tableCellBackgroundFill.solidFill.color"
or to
"fields": "*"
There is a second problem with your request:
When specifying the table range, it is required to set the properties rowSpan and columnSpan.
A complete, correct request would be:
{
"requests": [
{
"updateTableCellProperties": {
"objectId": "gf9d8fea71f_22_1",
"tableRange": {
"location": {
"columnIndex": 0,
"rowIndex": 1
},
"rowSpan": 1,
"columnSpan": 1
},
"fields": "tableCellBackgroundFill.solidFill.color",
"tableCellProperties": {
"tableCellBackgroundFill": {
"solidFill": {
"color": {
"themeColor": "LIGHT1"
}
}
}
}
}
}
]
}

Twitter API 2.0 - Unable to fetch user.fields

I am using API version 2.0 and unable to fetch the user.fields results. All other parameters seem to be returning results correctly. I'm following this documentation.
url = "https://api.twitter.com/2/tweets/search/all"
query_params = {
"query": "APPL",
"max_results": "10",
"tweet.fields": "created_at,lang,text,author_id",
"user.fields": "name,username,created_at,location",
"expansions": "referenced_tweets.id.author_id",
}
response = requests.request("GET", url, headers=headers, params=query_params).json()
Sample result:
{
'author_id': '1251347502013521925',
'text': 'All conspiracy. But watch for bad news on Apple. Such a vulnerable stocktechnically for the biggest market cap # $2.1T ( Thanks Jay). This is the glue for the bulls. But, they stopped innovating when Steve died, built a fancy office and split the stock. $appl',
'lang': 'en',
'created_at': '2021-06-05T02:33:48.000Z',
'id': '1401004298738311168',
'referenced_tweets': [{
'type': 'retweeted',
'id': '1401004298738311168'
}]
}
As you can see, the following information is not returned: name, username, and location.
Any idea how to retrieve this info?
Your query does actually return the correct data. I tested this myself.
A full example response will be structured like this:
{
"data": [
{
"created_at": "2021-06-05T02:33:48.000Z",
"lang": "en",
"id": "1401004298738311168",
"text": "All conspiracy. But watch for bad news on Apple. Such a vulnerable stocktechnically for the biggest market cap # $2.1T ( Thanks Jay). This is the glue for the bulls. But, they stopped innovating when Steve died, built a fancy office and split the stock. $appl",
"author_id": "1251347502013521925",
"referenced_tweets": [
{
"type": "retweeted",
"id": "1401004298738311168"
}
]
}
],
"includes": {
"users": [
{
"name": "Gary Casper",
"id": "1251347502013521925",
"username": "Hisel1979",
"created_at": "2020-07-11T13:39:58.000Z"
}
]
}
}
The sample result you provided comes from within the data object. However, the expanded object data will be nested in the includes object (in your case name, username, and location). The corresponding user object can be referenced via the author_id field.

Azure JSON subprotocol `json.webpubsub.azure.v1`to join a group fetch responses programmatically

I'm trying obtain programmatically if joining the group was successfully when using json.webpubsub.azure.v1 subprotocol to join a group with a sample.
My sample JSON below, which doesn't fetch outcome success response or error response:
Basically, I'm trying to identify if joining the group was successful with the json.webpubsub.azure.v1 subprotocol.
Sample request:
{
"type": "joinGroup",
"group": "group1",
}
Yes, you can, by add “ackId” when joining the group. When the request payload contains “ackId”, the service will return an ack response to the client containing the result for this action. Make sure you use incremental “ackId”s when sending different messages.
Sample request:
{
"type": "joinGroup",
"group": "group1",
"ackId" : 1
}
Sample success response:
{
"type": "ack",
"success": true,
"ackId" : 1
}
Sample error response:
{
"type": "ack",
"success": false,
"ackId" : 1,
"error":
{
"name": "Forbidden",
"description": "The client does not have permission to join group ‘group1’"
}
}

updateShapeProperties complaining about missing fields property

Details
OS: Manjaro Linux
Node.js version: 10
npm version: 6
googleapis version: 51.0.0
Steps to reproduce
We are sending a google slide API request with updateShapeProperties:
"updateShapeProperties": {
"objectId": objectId,
"shapeProperties": {
"shapeBackgroundFill": {
"solidFill": {
"color": {
"rgbColor": {
"red": r,
"green": g,
"blue": b
}
},
"alpha": 1
}
},
"outline": {
"outlineFill": {
"solidFill": {
"color": {
"rgbColor": {
"red": 0,
"green": 0,
"blue": 0
}
},
"alpha": 0.1
}
},
},
"contentAlignment": "MIDDLE"
},
"fields": "*"
}
where objectId comes from one of any pageElement we get from slide.pageElements
We get a 400 response that states:
message: ""Invalid requests[2].updateShapeProperties: At least one field must be listed in 'fields'. (Use '*' to indicate all fields.)
This somehow used to work the past few months, and was wondering why it suddenly doesn't work right now. Did we have any changes or updates on the updateShapeProperties API?
I believe that [2] of Invalid requests[2].updateShapeProperties is the request body in your question. When I tested your request body, I could replicate your situation. The error message is At least one field must be listed in 'fields'. (Use '*' to indicate all fields.). When I tested "fields": "", I got the same error message. From this situation, I thought that it might be a bug.
When I searched this situation at the Google issue tracker, I found it. Ref
So as the current workaround, in order to avoid this error, in your request body, how about the following modification?
From:
"fields": "*"
To:
"fields": "shapeBackgroundFill,outline,contentAlignment"

Create a sensor with SensorDataUnitType

I'm trying to create a new device with sensor(s).
This is the payload I'm sending to create the new device:
{"Name":"DeviceABC","HardwareId":"D4xxx425","SpaceId":"xxxx-xxx-xx-xx-xxx","Status":"Provisioned","CreateIoTHubDevice":false,"Properties":[{"Name":"VendorName","Value":"MyVendor"},{"Name":"VendorDeviceId","Value":"D4xxx19425"},{"Name":"VendorDeviceType","Value":"electricity"}],"Sensors":[{"pollRate":0,"id":null,"dataType":"Json","dataUnitType":"KilowattHourEnergy","deviceId":null,"portType":null,"port":"electricity","spaceId":null,"type":"Classic"}]}
In this case I want to use a built in type. KWh
{
"id": 186,
"category": "SensorDataUnitType",
"name": "KilowattHourEnergy",
"disabled": false,
"logicalOrder": 0,
"friendlyName": "kWh"
}
The SensorDataType is :
{
"id": 314,
"spaceId": "xxxx-xx-xxx-xx-xxxx",
"category": "SensorDataType",
"name": "Json",
"disabled": false,
"logicalOrder": 0
}
When I do this for other devices without specifying a dataUnitType in the sensor object, it works fine. But as soon as I include it i get this:
{
"error": {
"code": "400.600.000.000",
"message": "Invalid datatype/dataunittype combination used on sensor."
}}
There seems to be a naming convention between SensorDataType and SensorDataUnitType. The convention is that the SensorDataUnitType needs to end with the full SensorDataType. In your example you have:
"dataType":"Json",
"dataUnitType":"KilowattHourEnergy",
But according to the convention it should be
"dataType":"Json",
"dataUnitType":"KilowattHourEnergyJson",
Of course you can't just change KilowattHourEnergy because it's in a system ontology. So to get it working I had to create KilowattHourEnergyJson as a new type.
I guess the most complete answer would include that in your case the SensorDataType probably shouldn't be JSON but should be Energy.

Resources