Why am I obtaining this strange behavior when I call an API implemented in WSO2 ESB by WSO2 API Manager? ("Accept: application/json" header issue) - wso2

I am very new in WSO2 API Manager and I have the following problem registering some API (implemented using WSO2 ESB component) on an old WSO2 API Manager version 1.9.0).
The problem is the following one:
I register an API on the API Manager and in the try it tool it generate and perform a request like this:
curl -X GET --header "Accept: application/json" --header "Authorization: Bearer c86b19dac9dfd7406ebe9013373c9de9" "https://api.MY-COMPANY.org/api/dsa2/v1.0.0/market/12?lang=1"
This request is sent to the endpoint representing the ESB API implementation (I see it in the log). This ESB implementation contains a script mediator that do some works on a JSON object that the API will return to the user.
I am attaching the code fo my API for clarity:
<?xml version="1.0" encoding="UTF-8"?>
<api context="/market" name="market_details" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" protocol="http" uri-template="/{marketId}?lang={lang_id}">
<inSequence>
<log level="full"/>
<property expression="get-property('uri.var.marketId')" name="marketId" scope="default" type="STRING"/>
<property expression="get-property('uri.var.lang_id')" name="lang_id" scope="default" type="STRING"/>
<log level="custom">
<property expression="$ctx:marketId" name="Market ID"/>
<property expression="$ctx:lang_id" name="Lang ID"/>
</log>
<property name="messageType" scope="axis2" type="STRING" value="application/xml"/>
<payloadFactory media-type="xml">
<format>
<ds:GetMarketDetails xmlns:ds="http://ws.wso2.org/dataservice">
<ds:market_id>$1</ds:market_id>
<ds:language_id>$2</ds:language_id>
</ds:GetMarketDetails>
</format>
<args>
<arg evaluator="xml" expression="$ctx:marketId"/>
<arg evaluator="xml" expression="$ctx:lang_id"/>
</args>
</payloadFactory>
<header name="Action" scope="default" value="urn:GetMarketDetails"/>
<call>
<endpoint key="agrimarketprice_Endpoint"/>
</call>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
<property expression="json-eval($.)" name="JSONPayload" scope="default" type="STRING"/>
<script language="js"><![CDATA[var log = mc.getServiceLog();
// Problem is: null values returned from DSS as #nil=true and converted as Object into JSON.
// See https://wso2.org/jira/browse/ESBJAVA-4467 as example of bug, reported into JIRA
// So, we need convert values, what may be nulls with using this function
function checkForNull(value) {
if (value instanceof Object && "#nil" in value) {
return null;
}
return value;
}
// stange workaround for getting JSON Payload. getPayloadJSON returned null.
var pl_string = mc.getProperty("JSONPayload");
log.info("PAYLOAD STRING: " + pl_string);
var payload = JSON.parse(pl_string);
// create new response
var response = payload.Markets.Market;
//log.info("RESPONSE: " + JSON.stringify(response));
//response.id = mc.getProperty("MarketId");
response.id = mc.getProperty("marketId");
// convert null values
response.id = checkForNull(response.id);
response.regione = checkForNull(response.regione);
response.province = checkForNull(response.province);
response.city = checkForNull(response.city);
response.district = checkForNull(response.district);
response.town = checkForNull(response.town);
response.village = checkForNull(response.village);
if(response.commodities && response.commodities.commoditiesList) {
// convert array of commodities into required HATEOS format
var commodity = new Array();
for (i = 0; i < response.commodities.commoditiesList.length; ++i) {
var el = response.commodities.commoditiesList[i];
var newEl = new Object();
newEl.commodity_details_id = checkForNull(el.commodity_details_id);
newEl.commodity_name_en = checkForNull(el.commodity_name_en);
newEl.commodity_name = checkForNull(el.commodity_name);
newEl.description = checkForNull(el.description);
newEl.image_link = checkForNull(el.image_link);
newEl.market_commodity_details_id = checkForNull(el.market_commodity_details_id);
newEl.price_series_id = checkForNull(el.price_series_id);
newEl.last_price_date = checkForNull(el.last_price_date);
newEl.last_avg_price = checkForNull(el.last_avg_price);
newEl.currency = checkForNull(el.currency);
newEl.measure_unit = checkForNull(el.measure_unit);
commodityDetailsLinks = [];
commodityDetailsRefObj = {};
commodityDetailsRefObj.rel = "commodity_details";
commodityDetailsRefObj.href = "http://5.249.148.180:8280/commodity_details/" + checkForNull(el.commodity_details_id);
commodityDetailsRefObj.type = "GET";
commodityDetailsLinks.push(commodityDetailsRefObj);
newEl.links = commodityDetailsLinks;
commodity.push(newEl);
}
response.commodities.commoditiesList = commodity;
}
log.info("PROCESSED RESPONSE:");
//var jsonStr = JSON.stringify(response, null, 2); // spacing level = 2
//log.info(jsonStr);
selfLinks = [];
selfsRefObj = {};
selfsRefObj.rel = "self";
selfsRefObj.href = "http://5.249.148.180:8280/market/" + checkForNull(response.id);
selfsRefObj.type = "GET";
selfLinks.push(selfsRefObj);
response.links = selfLinks;
delete response.id;
var jsonStr = JSON.stringify(response, null, 2); // spacing level = 2
log.info("RESPONSE PROCESSED:");
log.info(jsonStr);
// put payload back
mc.setPayloadJSON(response);
log.info("This is the end");]]></script>
<property name="RESPONSE" scope="default" type="STRING" value="true"/>
<header action="remove" name="To" scope="default"/>
<send/>
</inSequence>
<outSequence>
<send/>
</outSequence>
<faultSequence/>
</resource>
</api>
In the JavaScript mediator happens something very strange.
Calling my API as done by the API Manager try it tool (IMPORTANT: the problem seems to be the --header "Accept: application/json" header):
curl -X GET --header "Accept: application/json" --header "Authorization: Bearer c86b19dac9dfd7406ebe9013373c9de9" "https://api.MY-COMPANY.org/api/dsa2/v1.0.0/market/12?lang=1"
I obtain this wrong JSON as value of the payload value (the value of my pl_string):
{
"Markets": {
"Market": [{
"market_id": 12,
"market_name": "Kouthiaba",
"market_description": "Kouthiaba Market",
"localization_id": "2",
"long": null,
"lat": null,
"country": "Senegal",
"regione": null,
"province": null,
"city": null,
"district": null,
"town": null,
"village": null,
"commodities": {
"commoditiesList": [{
"commodity_details_id": 43,
"commodity_name_en": "Millet",
"commodity_name": "Millet",
"description": "Millet",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fmillet.png?alt=media&token=ff9c67ab-e07a-4097-95ae-826fe1aa49ed",
"market_commodity_details_id": 178,
"price_series_id": 2494,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "150.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 13,
"commodity_name_en": "Sorghum",
"commodity_name": "Sorghum",
"description": "Sorghum",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fsorghum.png?alt=media&token=ae33f8e8-50c4-4b8e-868f-1997d50d7ad4",
"market_commodity_details_id": 179,
"price_series_id": 2495,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "160.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 11,
"commodity_name_en": "Maize",
"commodity_name": "Maize",
"description": "Maize is a staple food in many parts of the world, with a total production of 1040M tonnes. However, not all of the maize produced is for human consumption but it is also utilised in bio fuel production. Some of the maize produced is used for corn ethanol, animal feed and other maize products such as corn-starch and corn syrup. Maize for human consumption is used in five different forms 1) Popcorn 2) Flint corn 3) Dent corn 4) Floury corn and 5) Sweet corn.",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fmaize.png?alt=media&token=34d5a149-1721-47e9-863b-c939c7fd7419",
"market_commodity_details_id": 180,
"price_series_id": 2496,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "125.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 38,
"commodity_name_en": "Rice Ordinary",
"commodity_name": "Rice Ordinary",
"description": "Rice Ordinary",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Friz.png?alt=media&token=c35e7648-1793-423b-acd2-52d8a1e58c53",
"market_commodity_details_id": 181,
"price_series_id": 2497,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "285.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 39,
"commodity_name_en": "Rice Perfumed",
"commodity_name": "Rice Perfumed",
"description": "Rice Perfumed",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Friz.png?alt=media&token=c35e7648-1793-423b-acd2-52d8a1e58c53",
"market_commodity_details_id": 182,
"price_series_id": 2498,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "450.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 40,
"commodity_name_en": "Black Eyed Pea",
"commodity_name": "Black Eyed Pea",
"description": "Black Eyed Pea",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fblack_eyed_pea.png?alt=media&token=ab397785-68da-413a-978b-e0ebab8407b4",
"market_commodity_details_id": 183,
"price_series_id": 2499,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "325.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 42,
"commodity_name_en": "Peanut with Shell",
"commodity_name": "Peanut with Shell",
"description": "Peanut with Shell",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fpeanut.png?alt=media&token=6d5ded68-3126-44df-b429-89fe32483d2d",
"market_commodity_details_id": 184,
"price_series_id": 2500,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "175.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 41,
"commodity_name_en": "Peanut without Shell",
"commodity_name": "Peanut without Shell",
"description": "Peanut without Shell",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fpeanut_open.png?alt=media&token=d5af0a5c-6327-418e-9fac-19d34fcedaf5",
"market_commodity_details_id": 185,
"price_series_id": 2501,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": "325.0000",
"currency": "XOF",
"measure_unit": "kilogram"
}
]
}
}
]
}
}
This JSON broke my script because I have:
{
"Markets": {
"Market": [
..................
]
}
}
instead (as expected):
{
"Markets": {
"Market": {
................
}
}
}
The strange thing is that if I perform the same call to my API manager (using cURL in my shell) without set the --header "Accept: application/json" header, in this way:
curl -X GET --header "Authorization: Bearer c86b19dac9dfd7406ebe9013373c9de9" "https://api.MY-COMPANY.org/api/dsa/v1.0.0/market/12?lang=1"
I am obtaining the correct JSON and my script is not broken, infact my JSON payload (the value of my pl_string) now is:
{
"Markets": {
"Market": {
"market_id": 12,
"market_name": "Kouthiaba",
"market_description": "Kouthiaba Market",
"localization_id": 2,
"long": {
"#nil": "true"
},
"lat": {
"#nil": "true"
},
"country": "Senegal",
"regione": {
"#nil": "true"
},
"province": {
"#nil": "true"
},
"city": {
"#nil": "true"
},
"district": {
"#nil": "true"
},
"town": {
"#nil": "true"
},
"village": {
"#nil": "true"
},
"commodities": {
"commoditiesList": [{
"commodity_details_id": 43,
"commodity_name_en": "Millet",
"commodity_name": "Millet",
"description": "Millet",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fmillet.png?alt=media&token=ff9c67ab-e07a-4097-95ae-826fe1aa49ed",
"market_commodity_details_id": 178,
"price_series_id": 2494,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 150.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 13,
"commodity_name_en": "Sorghum",
"commodity_name": "Sorghum",
"description": "Sorghum",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fsorghum.png?alt=media&token=ae33f8e8-50c4-4b8e-868f-1997d50d7ad4",
"market_commodity_details_id": 179,
"price_series_id": 2495,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 160.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 11,
"commodity_name_en": "Maize",
"commodity_name": "Maize",
"description": "Maize is a staple food in many parts of the world, with a total production of 1040M tonnes. However, not all of the maize produced is for human consumption but it is also utilised in bio fuel production. Some of the maize produced is used for corn ethanol, animal feed and other maize products such as corn-starch and corn syrup. Maize for human consumption is used in five different forms 1) Popcorn 2) Flint corn 3) Dent corn 4) Floury corn and 5) Sweet corn.",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fmaize.png?alt=media&token=34d5a149-1721-47e9-863b-c939c7fd7419",
"market_commodity_details_id": 180,
"price_series_id": 2496,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 125.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 38,
"commodity_name_en": "Rice Ordinary",
"commodity_name": "Rice Ordinary",
"description": "Rice Ordinary",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Friz.png?alt=media&token=c35e7648-1793-423b-acd2-52d8a1e58c53",
"market_commodity_details_id": 181,
"price_series_id": 2497,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 285.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 39,
"commodity_name_en": "Rice Perfumed",
"commodity_name": "Rice Perfumed",
"description": "Rice Perfumed",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Friz.png?alt=media&token=c35e7648-1793-423b-acd2-52d8a1e58c53",
"market_commodity_details_id": 182,
"price_series_id": 2498,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 450.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 40,
"commodity_name_en": "Black Eyed Pea",
"commodity_name": "Black Eyed Pea",
"description": "Black Eyed Pea",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fblack_eyed_pea.png?alt=media&token=ab397785-68da-413a-978b-e0ebab8407b4",
"market_commodity_details_id": 183,
"price_series_id": 2499,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 325.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 42,
"commodity_name_en": "Peanut with Shell",
"commodity_name": "Peanut with Shell",
"description": "Peanut with Shell",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fpeanut.png?alt=media&token=6d5ded68-3126-44df-b429-89fe32483d2d",
"market_commodity_details_id": 184,
"price_series_id": 2500,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 175.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}, {
"commodity_details_id": 41,
"commodity_name_en": "Peanut without Shell",
"commodity_name": "Peanut without Shell",
"description": "Peanut without Shell",
"image_link": "https://firebasestorage.googleapis.com/v0/b/my-company-digital-services-portfolio.appspot.com/o/img%2Ficons%2Fagrimarket%2Fcommodity%2Fpeanut_open.png?alt=media&token=d5af0a5c-6327-418e-9fac-19d34fcedaf5",
"market_commodity_details_id": 185,
"price_series_id": 2501,
"last_price_date": "2018-03-18+01:00",
"last_avg_price": 325.0000,
"currency": "XOF",
"measure_unit": "kilogram"
}
]
}
}
}
}
and this not broke my script.
So, my questions are:
1) Why setting this header to my call my JSON (that is obtained by the result of a DSS query call converted in JSON format) is different?
2) Can I say tO WSO2 API MANAGER to avoid so set this ****--header "Accept: application/json"** header in the call?
Practically the best thing to do is that my API manager perform a call like this:
curl -X GET --header "Authorization: Bearer c86b19dac9dfd7406ebe9013373c9de9" "https://api.MY-COMPANY.org/api/dsa/v1.0.0/market/12?lang=1"
3) At the moment my API manager is performing this call:
curl -X GET --header "Accept: application/json" --header "Authorization: Bearer c86b19dac9dfd7406ebe9013373c9de9" "https://api.MY-COMPANY.org/api/dsa/v1.0.0/market/12?lang=1"
I think that this --header "Accept: application/json" header is propagated to the call to the ESB implementation of my API (correct me if I am doing wrong assertion).
In case can I remove this header into my API definition at the beginning of my API flow? Could be an idea?

the problem seems to be the --header "Accept: application/json" header
I assume you are trying the APIs out from the API Store UI, where the json response type is default. You can change it default value (or set list of supported values) in the the API Publusher's definition filling the response types for each resource
1) Why setting this header to my call my JSON (that is obtained by the result of a DSS query call converted in JSON format) is different?
I believe DSS uses different library (or library version) to convert XML to JSON. According to my experience try to avoid this conversion if possible (as XML doesn't explicitly contain value type information)
you can even disable this automatic conversion somewhere in axis2. xml (for API, DSS and ESB)
2) Can I say tO WSO2 API MANAGER to avoid so set this ****--header "Accept: application/json"** header in the call? Practically the best thing to do is that my API manager
as already mentioned you can set the default or supported values in the Publisher. The set response types are used only by the API Store, the clients may send anything..
3) At the moment my API manager is performing this call:
... --header "Accept: application/json" header is propagated to the call to the ESB implementation of my API (correct me if I am doing wrong assertion).
all headers (except Authorization) are propagated

Related

print s3 object content through lambda

I have a notification on an S3 bucket upload to place a message in an SQS queue. The SQS queue triggers a lambda function. I am trying to extract the content of the S3 object after a notification message is sent to SQS message. when the notification message arrives on SQS, a lambda is triggered and it should extract the contents of the file.
Following is the SQS response
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": {
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "us-east-1",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "example-bucket",
"ownerIdentity": {
"principalId": "EXAMPLE"
},
"arn": "arn:aws:s3:::example-bucket"
},
"object": {
"key": "access_log_20220908-102154.txt",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
},
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {},
"md5OfBody": "{{{md5_of_body}}}",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:XXXX:s3-OS-queue",
"awsRegion": "us-east-1"
}
]
}
Here is the code:
def lambda_handler(event, context):
#Loops through every file uploaded
for record in event['Records']:
#pull the body out & json load it
json_record=(record['body'])
try:
print(json_record)
except botocore.exceptions.ClientError as error:
print("raised error")
raise error
bucket = json_record['Records'][0]["s3"]["bucket"]["name"]
print(bucket)
key=json_record["Records"][0]["s3"]["object"]["key"]
print(key)
obj = s3.get_object(Bucket=bucket, Key=key)
print(obj)
#following part fails
body = obj['Body'].read()
print(body)
lines = body.splitlines()
print(lines)
```
printing the object returns a response metadata
```{'ResponseMetadata': {'RequestId': 'T2SAR2GW7WRP4B13', 'HostId': 'QVkghmfbKfXfiAP08SOeo9VDu9+HJxb+gs39/pBxDskJwcYVzdajDRWqhYdvJOo1/rNhMicWFvQ=', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amz-id-2': 'QVkghmfbKfXfiAP08SOeo9VDu9+HJxb+gs39/pBxDskJwcYVzdajDRWqhYdvJOo1/rNhMicWFvQ=', 'x-amz-request-id': 'T2SAR2GW7WRP4B13', 'date': 'Thu, 15 Sep 2022 23:49:15 GMT', 'last-modified': 'Thu, 08 Sep 2022 22:49:18 GMT', 'etag': '"c9bfa5ac6d2c2a2d19ec1544d564c277"', 'accept-ranges': 'bytes', 'content-type': 'text/plain', 'server': 'AmazonS3', 'content-length': '355891'}, 'RetryAttempts': 0}, 'AcceptRanges': 'bytes', 'LastModified': datetime.datetime(2022, 9, 8, 22, 49, 18, tzinfo=tzutc()), 'ContentLength': 355891, 'ETag': '"c9bfa5ac6d2c2a2d19ec1544d564c277"', 'ContentType': 'text/plain', 'Metadata': {}, 'Body': <botocore.response.StreamingBody object at 0x7fd20edbf250>}
error message
TypeError: string indices must be integers
Traceback (most recent call last):
File "/var/task/sample-bulk.py", line 42, in lambda_handler
bucket = json_record['Records'][0]["s3"]["bucket"]["name"]
It looks like get_object returns a object and key as a string which is not being accepted. Just for context, the object is a file that contains Apache access log. the goal is to eventually ingest the contents of that file to an index in OpenSearch

Data is not validated in Django Rest Framework

I am trying to pass the data to a serializer like the following:
myDict = {
"invoice_date": "2021-02-24T11:44:13+05:30",
"invoice_number": "12",
"vendor": "4",
"amount": "12",
"gst": "12",
"total_amount": "14",
"transaction_type": "Allot",
"status": "Hold",
"transactions": [
{
"t_no": 47,
"f_mile": "45",
"long_haul": "45",
"l_mile": "45",
"labour": "45",
"others": "54",
"a_t_no": 47,
},
{
"t_no": 102,
"f_mile": "12",
"long_haul": "12",
"l_mile": "21",
"labour": "21",
"others": "21",
"a_t_no": 102,
},
],
"owner": 2,
}
But when I check the validated data in the serialzer it shows it without the transactions data:
{'invoice_date': datetime.datetime(2021, 2, 24, 6, 14, 13, tzinfo=<UTC>), 'invoice_number': '12', 'amount': 12, 'gst': 12, 'total_amount': 14, 'status': 'Hold', 'transaction_type': 'Allot', 'vendor': <Vendor: Vendor object (4)>, 'owner': <User: yantraksh>}
so I tried to check the initial data that is being passed to the serializer :
<QueryDict: {
"invoice_date": ["2021-02-24T11:44:13+05:30"],
"invoice_number": ["12"],
"vendor": ["4"],
"amount": ["12"],
"gst": ["12"],
"total_amount": ["14"],
"transaction_type": ["Allot"],
"status": ["Hold"],
"transactions": [
'[{"t_no":47,"f_mile":"45","long_haul":"45","l_mile":"45","labour":"45","others":"54","a_t_no":47},{"t_no":102,"f_mile":"12","long_haul":"12","l_mile":"21","labour":"21","others": "21","a_t_no":102}]'
],
"owner": [2],
}>
It shows that the transaction data is being passed as a string, what should I change it to in order to get it as validated data ?
Based on the "evidence" in that QueryDict (namely strings wrapped in lists) it sounds like you're not submitting the data as JSON.
You can't use regular HTML form data to submit structured data, you'll need to post JSON data.

How to send a request?

I need to send a request with the following data
"order": {
"server_callback_url": "http://site.id/callback",
"currency": "UAH",
"amount": "1400",
"order_type": "settlement",
"response_url": "http://site.id/test/responsepage/",
"order_id": "test1234561467462099.19",
"operation_id": "test1234561467462099.19",
"order_desc": "test order",
"merchant_id": 700001,
"receiver": [
{
"requisites": {
"amount": 100,
"merchant_id": 500001
},
"type": "merchant"
},{
"requisites": {
"amount": 200,
"merchant_id": 600001
},
"type": "merchant"
},
]
}
I need to send them to https://api.fondy.eu/api/settlement
But I never did that. I am not familiar with DRF at all. Tell me how to implement it, please.
If we visit the endpoint, it says that only POST methods are allowed. We can make a POST request for example with the requests package.
import requests
data = {
"order": {
"server_callback_url": "http://site.id/callback",
"currency": "UAH",
"amount": "1400",
"order_type": "settlement",
"response_url": "http://site.id/test/responsepage/",
"order_id": "test1234561467462099.19",
"operation_id": "test1234561467462099.19",
"order_desc": "test order",
"merchant_id": 700001,
"receiver": [
{
"requisites": {
"amount": 100,
"merchant_id": 500001
},
"type": "merchant"
},{
"requisites": {
"amount": 200,
"merchant_id": 600001
},
"type": "merchant"
},
]
}
}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
response = requests.post('https://api.fondy.eu/api/settlement', data=data, headers=headers)
The header can be important, since it tells you in what format you make your reqest, and some APIs do not work if you do not set the Content-type properly.
The response is here a Response object, and you can parse the response to a Python dictionary with:
response.json()
locally this gives me:
{'response': {'error_code': 1002,
'error_message': 'Application error',
'request_id': 'iUxQzJfyBuxdI'}}
The status code is 200, so that probably means that the callback you specified was not valid (or some other items in your request).
You could use DRF as the documentation :
https://www.django-rest-framework.org/tutorial/2-requests-and-responses/
Or without DRF :
# importing the requests library
import requests
# api-endpoint
URL = "https://api.fondy.eu/api/settlement"
# defining a params dict for the parameters to be sent to the API
data =
"order": {
"server_callback_url": "http://site.id/callback",
"currency": "UAH",
"amount": "1400",
"order_type": "settlement",
"response_url": "http://site.id/test/responsepage/",
"order_id": "test1234561467462099.19",
"operation_id": "test1234561467462099.19",
"order_desc": "test order",
"merchant_id": 700001,
"receiver": [
{
"requisites": {
"amount": 100,
"merchant_id": 500001
},
"type": "merchant"
},{
"requisites": {
"amount": 200,
"merchant_id": 600001
},
"type": "merchant"
},
]
}
# sending get request and saving the response as response object
r = requests.POST(url = URL, data= data)
# extracting data in json format
data = r.json()
Maybe you would like to try the API before writing code, there is tool like postman to do this quickly :)

How do I format the API date fields in the template?

My routes look like this:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('deliverySchedule');
}
});
My API payload returns deliverySchedules like this (see below).
{
"delivery_schedules": [
{
"id": 47,
"from": "0001-01-01T09:00:00.000Z",
"to": "0001-01-01T10:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.663Z",
"updated_at": "2015-01-12T16:17:05.663Z"
},
{
"id": 62,
"from": "0001-01-01T09:00:00.000Z",
"to": "0001-01-01T10:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.684Z",
"updated_at": "2015-01-12T16:17:05.684Z"
},
{
"id": 48,
"from": "0001-01-01T10:00:00.000Z",
"to": "0001-01-01T11:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.665Z",
"updated_at": "2015-01-12T16:17:05.665Z"
},
{
"id": 63,
"from": "0001-01-01T10:00:00.000Z",
"to": "0001-01-01T11:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.685Z",
"updated_at": "2015-01-12T16:17:05.685Z"
},
{
"id": 49,
"from": "0001-01-01T11:00:00.000Z",
"to": "0001-01-01T12:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.666Z",
"updated_at": "2015-01-12T16:17:05.666Z"
},
{
"id": 64,
"from": "0001-01-01T11:00:00.000Z",
"to": "0001-01-01T12:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.686Z",
"updated_at": "2015-01-12T16:17:05.686Z"
},
{
"id": 50,
"from": "0001-01-01T12:00:00.000Z",
"to": "0001-01-01T13:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.668Z",
"updated_at": "2015-01-12T16:17:05.668Z"
},
{
"id": 65,
"from": "0001-01-01T12:00:00.000Z",
"to": "0001-01-01T13:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.687Z",
"updated_at": "2015-01-12T16:17:05.687Z"
},
{
"id": 66,
"from": "0001-01-01T13:00:00.000Z",
"to": "0001-01-01T14:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.688Z",
"updated_at": "2015-01-12T16:17:05.688Z"
},
{
"id": 51,
"from": "0001-01-01T13:00:00.000Z",
"to": "0001-01-01T14:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.669Z",
"updated_at": "2015-01-12T16:17:05.669Z"
},
{
"id": 67,
"from": "0001-01-01T14:00:00.000Z",
"to": "0001-01-01T15:00:00.000Z",
"period": "tomorrow",
"status": "available",
"created_at": "2015-01-12T16:17:05.689Z",
"updated_at": "2015-01-12T16:17:05.689Z"
},
{
"id": 52,
"from": "0001-01-01T14:00:00.000Z",
"to": "0001-01-01T15:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.670Z",
"updated_at": "2015-01-12T16:17:05.670Z"
},
{
"id": 53,
"from": "0001-01-01T15:00:00.000Z",
"to": "0001-01-01T16:00:00.000Z",
"period": "today",
"status": "available",
"created_at": "2015-01-12T16:17:05.672Z",
"updated_at": "2015-01-12T16:17:05.672Z"
}
]
}
I have a template that looks like this:
<ul>
{{#each deliverySchedule in model}}
<li>
{{deliverySchedule.from}} - {{deliverySchedule.from}} ({{deliverySchedule.status}})
</li>
{{/each}}
</ul>
I want deliverySchedule.from and deliverySchedule.to to display something like: 1pm - 2pm (available). Right now it is displaying as:
Tue Jan 01 1901 17:00:00 GMT+0800 (SGT) - Tue Jan 01 1901 17:00:00 GMT+0800 (SGT) available
What is the right way to go about this? Should I turn the results of delivery_schedules into a new array in a controller and use something like moment.js to convert the from and to values to 1pm and 2pm formats respectively?
Apparently, I need to register the custom helper, following http://www.ember-cli.com/#resolving-handlebars-helpers
// app/helpers/convert-date-time.js
import Ember from "ember";
export default Ember.Handlebars.makeBoundHelper(function(value, options) {
var date = new Date(value);
var hours = date.getHours();
var minutes = date.getMinutes();
var ampm = hours >= 12 ? 'pm' : 'am';
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? '0'+minutes : minutes;
var strTime = hours + ':' + minutes + ' ' + ampm;
return strTime;
});
In a template:
{{convert-date-time deliverySchedule.from}}
You should use handelbar helpers in order to format your dates :)
Here is the documentation about it :
http://emberjs.com/guides/templates/writing-helpers/
You can use moment.js in your helper however depending on your needs it's may be over killing :)
In ember-cli use the command ember generate helper yourhelpername it will create a file in
app/helpers/ which looks like the following :
import Ember from 'ember';
function myhelper(value) {
//in your case format the date here ...for example ....
//you can also use moment.js and so on...
var toReturn=(new Date(value).getHours())+1;
if(toReturn >11){
toReturn=toReturn + " pm";
}else{
toReturn=toReturn + " am";
}
return toReturn;
}
export { myhelper };
export default Ember.Handlebars.makeBoundHelper(myhelper);
You then use {{myhelper deliverySchedule.from}}
you can also extend the helper by adding second parameters for options
import Ember from 'ember';
function myhelper(value,format) {
//in your case format the date here ...for example ....
//you can also use moment.js and so on...
var myDate=new Date(value);
var toReturn="";
switch(format){
case "dmy" :
toReturn=myDate.getDate()+" "+(myDate.getMonth()+1)+" "+myDate.getFullYear();
break;
case "ymd" :
toReturn=myDate.getFullYear()+" "+(myDate.getMonth()+1)+" "+myDate.getDate();
break;
default :
toReturn=(myDate.getMonth()+1)+" "+myDate.getDate();
break;
}
return toReturn;
}
export { myhelper };
export default Ember.Handlebars.makeBoundHelper(myhelper);
and in your template use
{{myhelper deliverySchedule.from "dmy"}}

Facebook graph API, access location tags in photos and posts

On photos and posts I often see location tags pointing to a Facebook page representing a place. They are typically prepended by "near" , "at" or "in". Does anybody know of a way to access this data via the graph API or any other way?
Here is an example :
webpage, I made the photo public so everyone can see http://www.facebook.com/photo.php?pid=11671735&id=506482094
api call https://graph.facebook.com/10150601497547095?access_token=*
{
"id": "10150601497547095",
"from": {
"name": "Edouard Tabet",
"id": "506482094"
},
"name": "Alcohol grows on trees in Isabela, Galapagos",
"picture": "http://photos-a.ak.fbcdn.net/hphotos-ak-snc7/381095_10150601497547095_506482094_11671735_110244218_s.jpg",
"source": "http://a1.sphotos.ak.fbcdn.net/hphotos-ak-snc7/s720x720/381095_10150601497547095_506482094_11671735_110244218_n.jpg",
"height": 720,
"width": 479,
"images": [
{
"height": 2048,
"width": 1365,
"source": "http://a1.sphotos.ak.fbcdn.net/hphotos-ak-snc7/329294_10150601497547095_506482094_11671735_110244218_o.jpg"
},
{
"height": 720,
"width": 479,
"source": "http://a1.sphotos.ak.fbcdn.net/hphotos-ak-snc7/s720x720/381095_10150601497547095_506482094_11671735_110244218_n.jpg"
},
{
"height": 270,
"width": 180,
"source": "http://photos-a.ak.fbcdn.net/hphotos-ak-snc7/381095_10150601497547095_506482094_11671735_110244218_a.jpg"
},
{
"height": 130,
"width": 86,
"source": "http://photos-a.ak.fbcdn.net/hphotos-ak-snc7/381095_10150601497547095_506482094_11671735_110244218_s.jpg"
},
{
"height": 112,
"width": 75,
"source": "http://photos-a.ak.fbcdn.net/hphotos-ak-snc7/381095_10150601497547095_506482094_11671735_110244218_t.jpg"
}
],
"link": "http://www.facebook.com/photo.php?pid=11671735&id=506482094",
"icon": "http://static.ak.fbcdn.net/rsrc.php/v1/yz/r/StEh3RhPvjk.gif",
"created_time": "2012-01-16T23:34:54+0000",
"position": 1,
"updated_time": "2012-01-21T02:16:55+0000",
"comments": {
"data": [
{
"id": "10150601497547095_7207114",
"from": {
"name": "Tom LeNoble",
"id": "218686"
},
"message": "hope you are having fun!",
"can_remove": true,
"created_time": "2012-01-16T23:36:33+0000"
},
{
"id": "10150601497547095_7207963",
"from": {
"name": "Sol McKinney",
"id": "1021642751"
},
"message": "How come Darwin didn't write about that?!",
"can_remove": true,
"created_time": "2012-01-17T01:31:39+0000"
},
{
"id": "10150601497547095_7212820",
"from": {
"name": "Romain BL",
"id": "556337447"
},
"message": "Des bisous mr Tabet! J'esp\u00e8re que tu vas bien depuis tout ce temps!",
"can_remove": true,
"created_time": "2012-01-17T18:19:13+0000"
}
],
"paging": {
"next": "https://graph.facebook.com/10150601497547095/comments?access_token="
}
},
"likes": {
"data": [
{
"id": "1404245",
"name": "Hannah Russin"
},
{
"id": "1278210658",
"name": "Seth Long"
},
{
"id": "218686",
"name": "Tom LeNoble"
}
],
"paging": {
"next": "https://graph.facebook.com/10150601497547095/likes?access_token=&limit=25&offset=25&__after_id=218686"
}
}
}
I figured it out... When accessing a photo with the "photo id" the place attribute is not present in the result... when accessing the same photo through its "post id" then the place attribute appears in the JSON result of the GRAPH API call. Not very convenient...
They should all have place:
https://developers.facebook.com/tools/explorer?method=GET&path=15500414_689594654776